Quellcode durchsuchen

Update GameQ

Updated to commit 628534b19e7220a6d552ce83f025cf1d860e7068 from Austinb's Github repository
DieFeM vor 2 Jahren
Ursprung
Commit
39edf12df7
51 geänderte Dateien mit 2915 neuen und 219 gelöschten Zeilen
  1. 35 10
      protocol/GameQ/Buffer.php
  2. 5 0
      protocol/GameQ/Filters/Base.php
  3. 9 6
      protocol/GameQ/Filters/Stripcolors.php
  4. 21 21
      protocol/GameQ/GameQ.php
  5. 77 37
      protocol/GameQ/Protocols/Arma3.php
  6. 12 3
      protocol/GameQ/Protocols/Ase.php
  7. 48 0
      protocol/GameQ/Protocols/Avorion.php
  8. 49 0
      protocol/GameQ/Protocols/Barotrauma.php
  9. 42 0
      protocol/GameQ/Protocols/Blackmesa.php
  10. 199 0
      protocol/GameQ/Protocols/Cfx.php
  11. 110 0
      protocol/GameQ/Protocols/Cfxplayers.php
  12. 42 0
      protocol/GameQ/Protocols/Citadel.php
  13. 89 0
      protocol/GameQ/Protocols/Codmw2.php
  14. 221 0
      protocol/GameQ/Protocols/Doom3.php
  15. 1 1
      protocol/GameQ/Protocols/Egs.php
  16. 43 0
      protocol/GameQ/Protocols/Fof.php
  17. 6 122
      protocol/GameQ/Protocols/Gta5m.php
  18. 164 0
      protocol/GameQ/Protocols/Gtar.php
  19. 75 0
      protocol/GameQ/Protocols/Had2.php
  20. 42 0
      protocol/GameQ/Protocols/Halo.php
  21. 43 0
      protocol/GameQ/Protocols/Hl1.php
  22. 68 0
      protocol/GameQ/Protocols/Hll.php
  23. 43 0
      protocol/GameQ/Protocols/Kingpin.php
  24. 49 0
      protocol/GameQ/Protocols/Lifeisfeudal.php
  25. 219 0
      protocol/GameQ/Protocols/M2mp.php
  26. 62 0
      protocol/GameQ/Protocols/Minecraftbe.php
  27. 68 0
      protocol/GameQ/Protocols/Miscreated.php
  28. 43 0
      protocol/GameQ/Protocols/Modiverse.php
  29. 43 0
      protocol/GameQ/Protocols/Nmrih.php
  30. 43 0
      protocol/GameQ/Protocols/Of.php
  31. 183 0
      protocol/GameQ/Protocols/Openttd.php
  32. 7 0
      protocol/GameQ/Protocols/Pixark.php
  33. 50 0
      protocol/GameQ/Protocols/Postscriptum.php
  34. 84 0
      protocol/GameQ/Protocols/Quake4.php
  35. 166 0
      protocol/GameQ/Protocols/Raknet.php
  36. 50 0
      protocol/GameQ/Protocols/Rf2.php
  37. 43 0
      protocol/GameQ/Protocols/Rfactor.php
  38. 43 0
      protocol/GameQ/Protocols/Rfactor2.php
  39. 21 0
      protocol/GameQ/Protocols/Rust.php
  40. 50 0
      protocol/GameQ/Protocols/Sco.php
  41. 1 1
      protocol/GameQ/Protocols/Serioussam.php
  42. 2 2
      protocol/GameQ/Protocols/Sevendaystodie.php
  43. 1 1
      protocol/GameQ/Protocols/Source.php
  44. 50 0
      protocol/GameQ/Protocols/Stormworks.php
  45. 48 0
      protocol/GameQ/Protocols/Valheim.php
  46. 7 7
      protocol/GameQ/Protocols/Ventrilo.php
  47. 48 0
      protocol/GameQ/Protocols/Vrising.php
  48. 42 0
      protocol/GameQ/Protocols/Zomboid.php
  49. 25 0
      protocol/GameQ/Query/Core.php
  50. 7 1
      protocol/GameQ/Query/Native.php
  51. 16 7
      protocol/GameQ/Server.php

+ 35 - 10
protocol/GameQ/Buffer.php

@@ -262,8 +262,8 @@ class Buffer
      *
      * If not found, return everything
      *
-     * @param      $delims
-     * @param null $delimfound
+     * @param              $delims
+     * @param null|string &$delimfound
      *
      * @return string
      * @throws \GameQ\Exception\Protocol
@@ -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;
             }
         }
 
@@ -380,25 +380,30 @@ class Buffer
      * @return int
      * @throws \GameQ\Exception\Protocol
      */
-    public function readInt32()
+    public function readInt32($length = 4)
     {
-
         // Change the integer type we are looking up
+        $littleEndian = null;
         switch ($this->number_type) {
             case self::NUMBER_TYPE_BIGENDIAN:
-                $type = 'Nint';
+                $type = 'N';
+                $littleEndian = false;
                 break;
 
             case self::NUMBER_TYPE_LITTLEENDIAN:
-                $type = 'Vint';
+                $type = 'V';
+                $littleEndian = true;
                 break;
 
             default:
-                $type = 'Lint';
+                $type = 'L';
         }
 
+        // read from the buffer and append/prepend empty bytes for shortened int32
+        $corrected = $this->read($length);
+
         // Unpack the number
-        $int = unpack($type, $this->read(4));
+        $int = unpack($type . 'int', self::extendBinaryString($corrected, 4, $littleEndian));
 
         return $int['int'];
     }
@@ -498,4 +503,24 @@ class Buffer
 
         return $float['float'];
     }
+
+    private static function extendBinaryString($input, $length = 4, $littleEndian = null)
+    {
+        if (is_null($littleEndian)) {
+            $littleEndian = self::isLittleEndian();
+        }
+
+        $extension = str_repeat(pack($littleEndian ? 'V' : 'N', 0b0000), $length - strlen($input));
+
+        if ($littleEndian) {
+            return $input . $extension;
+        } else {
+            return $extension . $input;
+        }
+    }
+
+    private static function isLittleEndian()
+    {
+        return 0x00FF === current(unpack('v', pack('S', 0x00FF)));
+    }
 }

+ 5 - 0
protocol/GameQ/Filters/Base.php

@@ -46,6 +46,11 @@ abstract class Base
         $this->options = $options;
     }
 
+    public function getOptions()
+    {
+        return $this->options;
+    }
+
     /**
      * Apply the filter to the data
      *

+ 9 - 6
protocol/GameQ/Filters/Stripcolors.php

@@ -67,6 +67,9 @@ class Stripcolors extends Base
             case 'source':
                 array_walk_recursive($result, [$this, 'stripSource']);
                 break;
+            case 'gta5m':
+                array_walk_recursive($result, [$this, 'stripQuake']);
+                break;
         }
 
         /*$data['filtered'][ $server->id() ] = $result;
@@ -94,22 +97,22 @@ class Stripcolors extends Base
     }
 
     /**
-     * Strip color codes from Unreal based games
+     * Strip color codes from Source based games
      *
      * @param string $string
      */
-    protected function stripUnreal(&$string)
+    protected function stripSource(&$string)
     {
-        $string = preg_replace('/\x1b.../', '', $string);
+        $string = strip_tags($string);
     }
 
     /**
-     * Strip color codes from Source based games
+     * Strip color codes from Unreal based games
      *
      * @param string $string
      */
-    protected function stripSource(&$string)
+    protected function stripUnreal(&$string)
     {
-        $string = strip_tags($string);
+        $string = preg_replace('/\x1b.../', '', $string);
     }
 }

+ 21 - 21
protocol/GameQ/GameQ.php

@@ -41,6 +41,11 @@ use GameQ\Exception\Query as QueryException;
  */
 class GameQ
 {
+    /*
+     * Constants
+     */
+    const PROTOCOLS_DIRECTORY = __DIR__ . '/Protocols';
+
     /* Static Section */
 
     /**
@@ -119,12 +124,6 @@ class GameQ
      */
     public function __construct()
     {
-		
-		 /*
-		 * Constants
-		 */
-		define("PROTOCOLS_DIRECTORY", __DIR__ . '/Protocols');
-		
         // Check for missing utf8_encode function
         if (!function_exists('utf8_encode')) {
             throw new \Exception("PHP's utf8_encode() function is required - "
@@ -161,6 +160,16 @@ class GameQ
         return true;
     }
 
+    public function getServers()
+    {
+        return $this->servers;
+    }
+
+    public function getOptions()
+    {
+        return $this->options;
+    }
+
     /**
      * Chainable call to __set, uses set as the actual setter
      *
@@ -274,10 +283,6 @@ class GameQ
      *
      * @return $this
      */
-    public function setFilter($filterName, $options = []){
-		return $this->addFilter($filterName, $options);
-	} 
-     
     public function addFilter($filterName, $options = [])
     {
         // Create the filter hash so we can run multiple versions of the same filter
@@ -332,11 +337,6 @@ class GameQ
      * @return array
      * @throws \Exception
      */
-     
-    public function requestData(){
-		return $this->process();
-	}
-     
     public function process()
     {
 
@@ -420,10 +420,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);
                     }
                 }
 
@@ -521,13 +521,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
@@ -648,7 +648,7 @@ class GameQ
 
                 // Apply the filter to the data
                 $results = $filter->apply($results, $server);
-            } catch (\ReflectionException $e) {
+            } catch (\ReflectionException $exception) {
                 // Invalid, skip it
                 continue;
             }

+ 77 - 37
protocol/GameQ/Protocols/Arma3.php

@@ -32,24 +32,53 @@ use GameQ\Result;
  */
 class Arma3 extends Source
 {
+    // Base DLC names
+    const BASE_DLC_KART      = 'Karts';
+    const BASE_DLC_MARKSMEN  = 'Marksmen';
+    const BASE_DLC_HELI      = 'Helicopters';
+    const BASE_DLC_CURATOR   = 'Curator';
+    const BASE_DLC_EXPANSION = 'Expansion';
+    const BASE_DLC_JETS      = 'Jets';
+    const BASE_DLC_ORANGE    = 'Laws of War';
+    const BASE_DLC_ARGO      = 'Malden';
+    const BASE_DLC_TACOPS    = 'Tac-Ops';
+    const BASE_DLC_TANKS     = 'Tanks';
+    const BASE_DLC_CONTACT   = 'Contact';
+    const BASE_DLC_ENOCH     = 'Contact (Platform)';
+
+    // Special
+    const BASE_DLC_AOW       = 'Art of War';
+
+    // Creator DLC names
+    const CREATOR_DLC_GM     = 'Global Mobilization';
+    const CREATOR_DLC_VN     = 'S.O.G. Prairie Fire';
+    const CREATOR_DLC_CSLA   = 'ČSLA - Iron Curtain';
+    const CREATOR_DLC_WS     = 'Western Sahara';
+
     /**
-     * Defines the names for the specific game DLCs
+     * DLC Flags/Bits as defined in the documentation.
+     *
+     * @see https://community.bistudio.com/wiki/Arma_3:_ServerBrowserProtocol3
      *
      * @var array
      */
-    protected $dlcNames = [
-        'af82811b' => 'Karts',
-        '94f76a1a' => 'Marksmen',
-        'd0356eec' => 'Helicopters',
-        '19984a71' => 'Zeus',
-        '7fb4b1f3' => 'Apex',
-        '49c2c12b' => 'Jets',
-        '7e766e18' => 'Laws of War',
-        '99d71f90' => 'Malden',
-        'a8b10cdf' => 'Tac-Ops',
-        '37680ce8' => 'Tanks',
-        '43f9c377' => 'Contact',
-        'c4979557' => 'Enoch',
+    protected $dlcFlags = [
+        0b0000000000000001 => self::BASE_DLC_KART,
+        0b0000000000000010 => self::BASE_DLC_MARKSMEN,
+        0b0000000000000100 => self::BASE_DLC_HELI,
+        0b0000000000001000 => self::BASE_DLC_CURATOR,
+        0b0000000000010000 => self::BASE_DLC_EXPANSION,
+        0b0000000000100000 => self::BASE_DLC_JETS,
+        0b0000000001000000 => self::BASE_DLC_ORANGE,
+        0b0000000010000000 => self::BASE_DLC_ARGO,
+        0b0000000100000000 => self::BASE_DLC_TACOPS,
+        0b0000001000000000 => self::BASE_DLC_TANKS,
+        0b0000010000000000 => self::BASE_DLC_CONTACT,
+        0b0000100000000000 => self::BASE_DLC_ENOCH,
+        0b0001000000000000 => self::BASE_DLC_AOW,
+        0b0010000000000000 => 'Unknown',
+        0b0100000000000000 => 'Unknown',
+        0b1000000000000000 => 'Unknown',
     ];
 
     /**
@@ -111,11 +140,11 @@ class Arma3 extends Source
         $result = new Result();
 
         // Get results
-        $result->add('rules_protocol_version', $responseBuffer->readInt8());
-        $result->add('overflow', $responseBuffer->readInt8());
-        $dlcBit = decbin($responseBuffer->readInt8()); // Grab DLC bit 1 and use it later
-        $dlcBit2 = decbin($responseBuffer->readInt8()); // Grab DLC bit 2 and use it later
-        $dlcCount = substr_count($dlcBit, '1') + substr_count($dlcBit2, '1'); // Count the DLCs
+        $result->add('rules_protocol_version', $responseBuffer->readInt8()); // read protocol version
+        $result->add('overflow', $responseBuffer->readInt8()); // Read overflow flags
+        $dlcByte = $responseBuffer->readInt8(); // Grab DLC byte 1 and use it later
+        $dlcByte2 = $responseBuffer->readInt8(); // Grab DLC byte 2 and use it later
+        $dlcBits = ($dlcByte2 << 8) | $dlcByte; // concatenate DLC bits to 16 Bit int
 
         // Grab difficulty so we can man handle it...
         $difficulty = $responseBuffer->readInt8();
@@ -131,33 +160,44 @@ class Arma3 extends Source
         // Crosshair
         $result->add('crosshair', $responseBuffer->readInt8());
 
-        // Loop over the DLC bit so we can pull in the info for the DLC (if enabled)
-        for ($x = 0; $x < $dlcCount; $x++) {
-            $dlcHash = dechex($responseBuffer->readInt32());
-            isset($this->dlcNames[$dlcHash]) ?
-                $result->addSub('dlcs', 'name', $this->dlcNames[$dlcHash])
-                : $result->addSub('dlcs', 'name', 'Unknown');
-            $result->addSub('dlcs', 'hash', $dlcHash);
+        // Loop over the base DLC bits so we can pull in the info for the DLC (if enabled)
+        foreach ($this->dlcFlags as $dlcFlag => $dlcName) {
+            // Check that the DLC bit is enabled
+            if (($dlcBits & $dlcFlag) === $dlcFlag) {
+                // Add the DLC to the list
+                $result->addSub('dlcs', 'name', $dlcName);
+                $result->addSub('dlcs', 'hash', dechex($responseBuffer->readInt32()));
+            }
         }
 
-        // No longer needed
-        unset($dlcBit, $dlcBit2, $dlcCount, $dlcHash);
-
-        // Grab the mod count
+        // Read the mount of mods, these include DLC as well as Creator DLC and custom modifications
         $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
+        
+        // Loop over the mods
+        while ($modCount) {
+            // Read the mods hash
             $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));
+
+            // Get the information byte containing DLC flag and steamId length
+            $infoByte = $responseBuffer->readInt8();
+
+            // Determine isDLC by flag, first bit in upper nibble
+            $result->addSub('mods', 'dlc', ($infoByte & 0b00010000) === 0b00010000);
+            
+            // Read the steam id of the mod/CDLC (might be less than 4 bytes)
+            $result->addSub('mods', 'steam_id', $responseBuffer->readInt32($infoByte & 0x0F));
+
+            // Read the name of the mod
+            $result->addSub('mods', 'name', $responseBuffer->readPascalString(0, true) ?: 'Unknown');
+
+            --$modCount;
         }
 
-        unset($modCount, $x);
+        // No longer needed
+        unset($dlcByte, $dlcByte2, $dlcBits);
 
         // Get the signatures count
         $signatureCount = $responseBuffer->readInt8();

+ 12 - 3
protocol/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();

+ 48 - 0
protocol/GameQ/Protocols/Avorion.php

@@ -0,0 +1,48 @@
+<?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;
+
+/**
+ * Avorion Protocol Class
+ *
+ * @package GameQ\Protocols
+ */
+class Avorion extends Source
+{
+    /**
+     * String name of this protocol class
+     *
+     * @type string
+     */
+    protected $name = 'avorion';
+
+    /**
+     * Longer string name of this protocol class
+     *
+     * @type string
+     */
+    protected $name_long = "Avorion";
+
+    /**
+     * query_port = client_port + 1
+     *
+     * @type int
+     * protected $port_diff = 1;
+     */
+}

+ 49 - 0
protocol/GameQ/Protocols/Barotrauma.php

@@ -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;
+
+/**
+ * Barotrauma Protocol Class
+ *
+ * @package GameQ\Protocols
+ * @author Jesse Lukas <[email protected]>
+ */
+class Barotrauma extends Source
+{
+    /**
+     * String name of this protocol class
+     *
+     * @type string
+     */
+    protected $name = 'barotrauma';
+
+    /**
+     * Longer string name of this protocol class
+     *
+     * @type string
+     */
+    protected $name_long = "Barotrauma";
+
+    /**
+     * query_port = client_port + 1
+     *
+     * @type int
+     */
+    protected $port_diff = 1;
+}

+ 42 - 0
protocol/GameQ/Protocols/Blackmesa.php

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

+ 199 - 0
protocol/GameQ/Protocols/Cfx.php

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

+ 110 - 0
protocol/GameQ/Protocols/Cfxplayers.php

@@ -0,0 +1,110 @@
+<?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\Protocols\Http;
+
+/**
+ * GTA Five M Protocol Class
+ *
+ * Server base can be found at https://fivem.net/
+ *
+ * Based on code found at https://github.com/LiquidObsidian/fivereborn-query
+ *
+ * @author Austin Bischoff <[email protected]>
+ *
+ * Adding FiveM Player List by
+ * @author Jesse Lukas <[email protected]>
+ */
+
+class CFXPlayers extends Http
+{
+    /**
+     * Holds the real ip so we can overwrite it back
+     *
+     * @var string
+     */
+    protected $realIp = null;
+
+    /**
+     * Holds the real port so we can overwrite it back
+     *
+     * @var int
+     */
+    protected $realPortQuery = null;
+
+    /**
+     * Packets to send
+     *
+     * @var array
+     */
+    protected $packets = [
+        self::PACKET_STATUS => "GET /players.json HTTP/1.0\r\nAccept: */*\r\n\r\n", // Player List
+    ];
+
+    /**
+     * The protocol being used
+     *
+     * @var string
+     */
+    protected $protocol = 'cfxplayers';
+
+    /**
+     * String name of this protocol class
+     *
+     * @var string
+     */
+    protected $name = 'cfxplayers';
+
+    /**
+     * Longer string name of this protocol class
+     *
+     * @var string
+     */
+    protected $name_long = "cfxplayers";
+
+    /**
+     * Process the response
+     *
+     * @return array
+     * @throws Exception
+     */
+    public function processResponse()
+    {
+        // Make sure we have any players
+        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], true)) === null) {
+            throw new Exception(__METHOD__ . " JSON response from Stationeers protocol is invalid.");
+        }
+
+        // Return json as it should already be well formed
+        return [
+            'players' => $json,
+        ];
+    }
+}

+ 42 - 0
protocol/GameQ/Protocols/Citadel.php

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

+ 89 - 0
protocol/GameQ/Protocols/Codmw2.php

@@ -0,0 +1,89 @@
+<?php
+/**
+ * This file is part of GameQ.
+ *
+ * GameQ is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GameQ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace GameQ\Protocols;
+
+use GameQ\Buffer;
+use GameQ\Result;
+
+/**
+ * Call of Duty: Modern Warfare 2 Protocol Class
+ *
+ * @package GameQ\Protocols
+ * @author  Wilson Jesus <>
+ */
+class Codmw2 extends Quake3
+{
+    /**
+     * String name of this protocol class
+     *
+     * @type string
+     */
+    protected $name = 'codmw2';
+
+    /**
+     * Longer string name of this protocol class
+     *
+     * @type string
+     */
+    protected $name_long = "Call of Duty: Modern Warfare 2";
+    
+    protected function processPlayers(Buffer $buffer)
+    {
+        // Temporarily cache players in order to remove last
+        $players = [];
+
+        // Loop until we are out of data
+        while ($buffer->getLength()) {
+            // Make a new buffer with this block
+            $playerInfo = new Buffer($buffer->readString("\x0A"));
+
+            // Read player info
+            $player = [
+                'frags' => $playerInfo->readString("\x20"),
+                'ping' => $playerInfo->readString("\x20"),
+            ];
+
+            // Skip first "
+            $playerInfo->skip(1);
+
+            // Add player name, encoded
+            $player['name'] = utf8_encode(trim(($playerInfo->readString('"'))));
+
+            // Add player
+            $players[] = $player;
+        }
+
+        // Remove last, empty player
+        array_pop($players);
+
+        // Set the result to a new result instance
+        $result = new Result();
+
+        // Add players
+        $result->add('players', $players);
+
+        // Add Playercount
+        $result->add('clients', count($players));
+        
+        // Clear
+        unset($buffer, $players);
+
+        return $result->fetch();
+    }
+}

+ 221 - 0
protocol/GameQ/Protocols/Doom3.php

@@ -0,0 +1,221 @@
+<?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;
+
+/**
+ * Doom3 Protocol Class
+ *
+ * Handles processing DOOM 3 servers
+ *
+ * @package GameQ\Protocols
+ * @author Wilson Jesus <>
+ */
+class Doom3 extends Protocol
+{
+    /**
+     * Array of packets we want to look up.
+     * Each key should correspond to a defined method in this or a parent class
+     *
+     * @type array
+     */
+    protected $packets = [
+        self::PACKET_ALL => "\xFF\xFFgetInfo\x00PiNGPoNG\x00",
+    ];
+
+    /**
+     * Use the response flag to figure out what method to run
+     *
+     * @type array
+     */
+    protected $responses = [
+        "\xFF\xFFinfoResponse" => 'processStatus',
+    ];
+
+    /**
+     * The query protocol used to make the call
+     *
+     * @type string
+     */
+    protected $protocol = 'doom3';
+
+    /**
+     * String name of this protocol class
+     *
+     * @type string
+     */
+    protected $name = 'doom3';
+
+    /**
+     * Longer string name of this protocol class
+     *
+     * @type string
+     */
+    protected $name_long = "Doom 3";
+
+    /**
+     * The client join link
+     *
+     * @type string
+     */
+    protected $join_link = null;
+
+    /**
+     * Normalize settings for this protocol
+     *
+     * @type array
+     */
+    protected $normalize = [
+        // General
+        'general' => [
+            // target       => source
+            'hostname'   => 'si_name',
+            'gametype'   => 'gamename',
+            'mapname'    => 'si_map',
+            'maxplayers' => 'si_maxPlayers',
+            'numplayers' => 'clients',
+            'password'   => 'si_usepass',
+        ],
+        // Individual
+        'player'  => [
+            'name'  => 'name',
+            'ping'  => 'ping',
+        ],
+    ];
+
+    /**
+     * Handle response from the server
+     *
+     * @return mixed
+     * @throws Exception
+     */
+    public function processResponse()
+    {
+        // Make a buffer
+        $buffer = new Buffer(implode('', $this->packets_response));
+
+        // Grab the header
+        $header = $buffer->readString();
+
+        // Header
+        // Figure out which packet response this is
+        if (empty($header) || !array_key_exists($header, $this->responses)) {
+            throw new Exception(__METHOD__ . " response type '" . bin2hex($header) . "' is not valid");
+        }
+
+        return call_user_func_array([$this, $this->responses[$header]], [$buffer]);
+    }
+
+    /**
+     * Process the status response
+     *
+     * @param Buffer $buffer
+     *
+     * @return array
+     */
+    protected function processStatus(Buffer $buffer)
+    {
+        // We need to split the data and offload
+        $results = $this->processServerInfo($buffer);
+
+        $results = array_merge_recursive(
+            $results,
+            $this->processPlayers($buffer)
+        );
+
+        unset($buffer);
+
+        // Return results
+        return $results;
+    }
+
+    /**
+     * Handle processing the server information
+     *
+     * @param Buffer $buffer
+     *
+     * @return array
+     */
+    protected function processServerInfo(Buffer $buffer)
+    {
+        // Set the result to a new result instance
+        $result = new Result();
+
+        $result->add('version', $buffer->readInt8() . '.' . $buffer->readInt8());
+
+        // Key / value pairs, delimited by an empty pair
+        while ($buffer->getLength()) {
+            $key = trim($buffer->readString());
+            $val = utf8_encode(trim($buffer->readString()));
+
+            // Something is empty so we are done
+            if (empty($key) && empty($val)) {
+                break;
+            }
+
+            $result->add($key, $val);
+        }
+
+        unset($buffer);
+
+        return $result->fetch();
+    }
+
+    /**
+     * Handle processing of player data
+     *
+     * @param Buffer $buffer
+     *
+     * @return array
+     */
+    protected function processPlayers(Buffer $buffer)
+    {
+        // Some games do not have a number of current players
+        $playerCount = 0;
+
+        // Set the result to a new result instance
+        $result = new Result();
+
+        // Parse players
+        // Loop thru the buffer until we run out of data
+        while (($id = $buffer->readInt8()) != 32) {
+            // Add player info results
+            $result->addPlayer('id', $id);
+            $result->addPlayer('ping', $buffer->readInt16());
+            $result->addPlayer('rate', $buffer->readInt32());
+            // Add player name, encoded
+            $result->addPlayer('name', utf8_encode(trim($buffer->readString())));
+
+            // Increment
+            $playerCount++;
+        }
+
+        // Add the number of players to the result
+        $result->add('clients', $playerCount);
+
+        // Clear
+        unset($buffer, $playerCount);
+
+        return $result->fetch();
+    }
+}

+ 1 - 1
protocol/GameQ/Protocols/Egs.php

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

+ 43 - 0
protocol/GameQ/Protocols/Fof.php

@@ -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 Fistful of Frags
+ *
+ * @package GameQ\Protocols
+ * @author Austin Bischoff <[email protected]>
+ * @author Jesse Lukas <[email protected]>
+*/
+class Fof extends Source
+{
+    /**
+     * String name of this protocol class
+     *
+     * @type string
+     */
+    protected $name = 'fof';
+
+    /**
+     * Longer string name of this protocol class
+     *
+     * @type string
+     */
+    protected $name_long = "Fistful of Frags";
+}

+ 6 - 122
protocol/GameQ/Protocols/Gta5m.php

@@ -18,11 +18,6 @@
 
 namespace GameQ\Protocols;
 
-use GameQ\Buffer;
-use GameQ\Exception\Protocol as Exception;
-use GameQ\Protocol;
-use GameQ\Result;
-
 /**
  * GTA Five M Protocol Class
  *
@@ -31,29 +26,14 @@ use GameQ\Result;
  * Based on code found at https://github.com/LiquidObsidian/fivereborn-query
  *
  * @author Austin Bischoff <[email protected]>
+ *
+ * Adding FiveM Player List by
+ * @author Jesse Lukas <[email protected]>
+ *
+ * @deprecated Use protocol CFX for querying your CitizenFX server
  */
-class Gta5m extends Protocol
+class Gta5m extends Cfx
 {
-
-    /**
-     * Array of packets we want to look up.
-     * Each key should correspond to a defined method in this or a parent class
-     *
-     * @type array
-     */
-    protected $packets = [
-        self::PACKET_STATUS => "\xFF\xFF\xFF\xFFgetinfo xxx",
-    ];
-
-    /**
-     * Use the response flag to figure out what method to run
-     *
-     * @type array
-     */
-    protected $responses = [
-        "\xFF\xFF\xFF\xFFinfoResponse" => "processStatus",
-    ];
-
     /**
      * The query protocol used to make the call
      *
@@ -74,100 +54,4 @@ class Gta5m extends Protocol
      * @type string
      */
     protected $name_long = "GTA Five M";
-
-    /**
-     * Normalize settings for this protocol
-     *
-     * @type array
-     */
-    protected $normalize = [
-        // General
-        'general' => [
-            // target       => source
-            'gametype'   => 'gametype',
-            'hostname'   => 'hostname',
-            'mapname'    => 'mapname',
-            'maxplayers' => 'sv_maxclients',
-            'mod'        => 'gamename',
-            'numplayers' => 'clients',
-            'password'   => 'privateClients',
-        ],
-    ];
-
-    /**
-     * Process the response
-     *
-     * @return array
-     * @throws \GameQ\Exception\Protocol
-     */
-    public function processResponse()
-    {
-        // In case it comes back as multiple packets (it shouldn't)
-        $buffer = new Buffer(implode('', $this->packets_response));
-
-        // Figure out what packet response this is for
-        $response_type = $buffer->readString(PHP_EOL);
-
-        // Figure out which packet response this is
-        if (empty($response_type) || !array_key_exists($response_type, $this->responses)) {
-            throw new Exception(__METHOD__ . " response type '{$response_type}' is not valid");
-        }
-
-        // Offload the call
-        $results = call_user_func_array([$this, $this->responses[$response_type]], [$buffer]);
-
-        return $results;
-    }
-
-    /*
-     * Internal methods
-     */
-
-    /**
-     * Handle processing the status response
-     *
-     * @param Buffer $buffer
-     *
-     * @return array
-     */
-    protected function processStatus(Buffer $buffer)
-    {
-        // Set the result to a new result instance
-        $result = new Result();
-
-        // Lets peek and see if the data starts with a \
-        if ($buffer->lookAhead(1) == '\\') {
-            // Burn the first one
-            $buffer->skip(1);
-        }
-
-        // Explode the data
-        $data = explode('\\', $buffer->getBuffer());
-
-        // No longer needed
-        unset($buffer);
-
-        $itemCount = count($data);
-
-        // Now lets loop the array
-        for ($x = 0; $x < $itemCount; $x += 2) {
-            // Set some local vars
-            $key = $data[$x];
-            $val = $data[$x + 1];
-
-            if (in_array($key, ['challenge'])) {
-                continue; // skip
-            }
-
-            // Regular variable so just add the value.
-            $result->add($key, $val);
-        }
-
-        /*var_dump($data);
-        var_dump($result->fetch());
-
-        exit;*/
-
-        return $result->fetch();
-    }
 }

+ 164 - 0
protocol/GameQ/Protocols/Gtar.php

@@ -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 <[email protected]>
+ * @author Austin Bischoff <[email protected]>
+ */
+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();
+    }
+}

+ 75 - 0
protocol/GameQ/Protocols/Had2.php

@@ -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;
+
+/**
+ * Hidden & Dangerous 2 Protocol Class
+ *
+ * @author Wilson Jesus <>
+ */
+class Had2 extends Gamespy2
+{
+
+    /**
+     * String name of this protocol class
+     *
+     * @type string
+     */
+    protected $name = 'had2';
+
+    /**
+     * Longer string name of this protocol class
+     *
+     * @type string
+     */
+    protected $name_long = "Hidden & Dangerous 2";
+
+    /**
+     * The difference between the client port and query port
+     *
+     * @type int
+     */
+    protected $port_diff = 3;
+
+    /**
+     * Normalize settings for this protocol
+     *
+     * @type array
+     */
+    protected $normalize = [
+        // General
+        'general' => [
+            // target       => source
+            'dedicated'  => 'isdedicated',
+            'gametype'   => 'gametype',
+            'hostname'   => 'hostname',
+            'mapname'    => 'mapname',
+            'maxplayers' => 'maxplayers',
+            'numplayers' => 'numplayers',
+            'password'   => 'password',
+        ],
+        // Individual
+        'player'  => [
+            'name'  => 'player',
+            'score' => 'score',
+            'deaths' => 'deaths',
+            'ping' => 'ping',
+        ],
+    ];
+}

+ 42 - 0
protocol/GameQ/Protocols/Halo.php

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

+ 43 - 0
protocol/GameQ/Protocols/Hl1.php

@@ -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 Hl1
+ *
+ * @package GameQ\Protocols
+ * @author Austin Bischoff <[email protected]>
+ * @author Jesse Lukas <[email protected]>
+ */
+class Hl1 extends Source
+{
+    /**
+     * String name of this protocol class
+     *
+     * @type string
+     */
+    protected $name = 'hl1';
+
+    /**
+     * Longer string name of this protocol class
+     *
+     * @type string
+     */
+    protected $name_long = "Half Life";
+}

+ 68 - 0
protocol/GameQ/Protocols/Hll.php

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

+ 43 - 0
protocol/GameQ/Protocols/Kingpin.php

@@ -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;
+
+/**
+ * Kingpin: Life of Crime Protocol Class
+ *
+ * @package GameQ\Protocols
+ *
+ * @author  Wilson Jesus <>
+ */
+class Kingpin extends Quake2
+{
+    /**
+     * String name of this protocol class
+     *
+     * @type string
+     */
+    protected $name = 'kingpin';
+
+    /**
+     * Longer string name of this protocol class
+     *
+     * @type string
+     */
+    protected $name_long = "Kingpin: Life of Crime";
+}

+ 49 - 0
protocol/GameQ/Protocols/Lifeisfeudal.php

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

+ 219 - 0
protocol/GameQ/Protocols/M2mp.php

@@ -0,0 +1,219 @@
+<?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;
+
+/**
+ * Mafia 2 Multiplayer Protocol Class
+ *
+ * Loosely based on SAMP protocol
+ *
+ * Query port = server port + 1
+ *
+ * Handles processing Mafia 2 Multiplayer servers
+ *
+ * @package GameQ\Protocols
+ * @author Wilson Jesus <>
+ */
+class M2mp extends Protocol
+{
+    /**
+     * Array of packets we want to look up.
+     * Each key should correspond to a defined method in this or a parent class
+     *
+     * @type array
+     */
+    protected $packets = [
+        self::PACKET_ALL => "M2MP",
+    ];
+
+    /**
+     * Use the response flag to figure out what method to run
+     *
+     * @type array
+     */
+    protected $responses = [
+        "M2MP" => 'processStatus',
+    ];
+
+    /**
+     * The query protocol used to make the call
+     *
+     * @type string
+     */
+    protected $protocol = 'm2mp';
+
+    /**
+     * String name of this protocol class
+     *
+     * @type string
+     */
+    protected $name = 'm2mp';
+
+    /**
+     * Longer string name of this protocol class
+     *
+     * @type string
+     */
+    protected $name_long = "Mafia 2 Multiplayer";
+
+    /**
+     * The client join link
+     *
+     * @type string
+     */
+    protected $join_link = null;
+
+    /**
+     * The difference between the client port and query port
+     *
+     * @type int
+     */
+    protected $port_diff = 1;
+
+    /**
+     * Normalize settings for this protocol
+     *
+     * @type array
+     */
+    protected $normalize = [
+        // General
+        'general' => [
+            // target       => source
+            'hostname'   => 'servername',
+            'gametype'   => 'gamemode',
+            'maxplayers' => 'max_players',
+            'numplayers' => 'num_players',
+            'password'   => 'password',
+        ],
+        // Individual
+        'player'  => [
+            'name'  => 'name',
+        ],
+    ];
+
+    /**
+     * Handle response from the server
+     *
+     * @return mixed
+     * @throws Exception
+     */
+    public function processResponse()
+    {
+        // Make a buffer
+        $buffer = new Buffer(implode('', $this->packets_response));
+
+        // Grab the header
+        $header = $buffer->read(4);
+
+        // Header
+        // Figure out which packet response this is
+        if ($header != "M2MP") {
+            throw new Exception(__METHOD__ . " response type '" . bin2hex($header) . "' is not valid");
+        }
+
+        return call_user_func_array([$this, $this->responses[$header]], [$buffer]);
+    }
+
+    /**
+     * Process the status response
+     *
+     * @param Buffer $buffer
+     *
+     * @return array
+     */
+    protected function processStatus(Buffer $buffer)
+    {
+        // We need to split the data and offload
+        $results = $this->processServerInfo($buffer);
+
+        $results = array_merge_recursive(
+            $results,
+            $this->processPlayers($buffer)
+        );
+
+        unset($buffer);
+
+        // Return results
+        return $results;
+    }
+
+    /**
+     * Handle processing the server information
+     *
+     * @param Buffer $buffer
+     *
+     * @return array
+     */
+    protected function processServerInfo(Buffer $buffer)
+    {
+        // Set the result to a new result instance
+        $result = new Result();
+
+        // Always dedicated
+        $result->add('dedicated', 1);
+
+        // Pull out the server information
+        // Note the length information is incorrect, we correct using offset options in pascal method
+        $result->add('servername', $buffer->readPascalString(1, true));
+        $result->add('num_players', $buffer->readPascalString(1, true));
+        $result->add('max_players', $buffer->readPascalString(1, true));
+        $result->add('gamemode', $buffer->readPascalString(1, true));
+        $result->add('password', (bool) $buffer->readInt8());
+
+        unset($buffer);
+
+        return $result->fetch();
+    }
+
+    /**
+     * Handle processing of player data
+     *
+     * @param Buffer $buffer
+     *
+     * @return array
+     */
+    protected function processPlayers(Buffer $buffer)
+    {
+        // Set the result to a new result instance
+        $result = new Result();
+
+        // Parse players
+        // Read the player info, it's in the same query response for some odd reason.
+        while ($buffer->getLength()) {
+            // Check to see if we ran out of info, length bug from response
+            if ($buffer->getLength() <= 1) {
+                break;
+            }
+
+            // Only player name information is available
+            // Add player name, encoded
+            $result->addPlayer('name', utf8_encode(trim($buffer->readPascalString(1, true))));
+        }
+
+        // Clear
+        unset($buffer);
+
+        return $result->fetch();
+    }
+}

+ 62 - 0
protocol/GameQ/Protocols/Minecraftbe.php

@@ -0,0 +1,62 @@
+<?php
+/**
+ * This file is part of GameQ.
+ *
+ * GameQ is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GameQ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace GameQ\Protocols;
+
+/**
+ * Minecraft Bedrock Edition (BE) Protocol Class
+ *
+ * @package GameQ\Protocols
+ *
+ * @author  Austin Bischoff <[email protected]>
+ */
+class Minecraftbe extends Raknet
+{
+
+    /**
+     * String name of this protocol class
+     *
+     * @type string
+     */
+    protected $name = 'minecraftbe';
+
+    /**
+     * Longer string name of this protocol class
+     *
+     * @type string
+     */
+    protected $name_long = "Minecraft Bedrock Edition";
+
+    /**
+     * Normalize settings for this protocol
+     *
+     * @type array
+     */
+    protected $normalize = [
+        // General
+        'general' => [
+            // target       => source
+            'dedicated'  => 'dedicated',
+            'gametype'   => 'edition',
+            'hostname'   => 'motd_line_1',
+            'maxplayers' => 'max_players',
+            'mod'        => 'gamemode',
+            'numplayers' => 'num_players',
+        ],
+    ];
+}

+ 68 - 0
protocol/GameQ/Protocols/Miscreated.php

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

+ 43 - 0
protocol/GameQ/Protocols/Modiverse.php

@@ -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 Modiverse
+ *
+ * @package GameQ\Protocols
+ * @author Austin Bischoff <[email protected]>
+ * @author Jesse Lukas <[email protected]>
+ */
+class Modiverse extends Source
+{
+    /**
+     * String name of this protocol class
+     *
+     * @type string
+     */
+    protected $name = 'modiverse';
+
+    /**
+     * Longer string name of this protocol class
+     *
+     * @type string
+     */
+    protected $name_long = "Modiverse";
+}

+ 43 - 0
protocol/GameQ/Protocols/Nmrih.php

@@ -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 No More Room in Hell
+ *
+ * @package GameQ\Protocols
+ * @author Austin Bischoff <[email protected]>
+ * @author Jesse Lukas <[email protected]>
+ */
+class Nmrih extends Source
+{
+    /**
+     * No More Room in Hell protocol class
+     *
+     * @type string
+     */
+    protected $name = 'nmrih';
+
+    /**
+     * Longer string name of this protocol class
+     *
+     * @type string
+     */
+    protected $name_long = "No More Room in Hell";
+}

+ 43 - 0
protocol/GameQ/Protocols/Of.php

@@ -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 Open Fortress
+ *
+ * @package GameQ\Protocols
+ * @author Austin Bischoff <[email protected]>
+ * @author Jesse Lukas <[email protected]>
+ */
+class Of extends Source
+{
+    /**
+     * Open Fortress protocol class
+     *
+     * @type string
+     */
+    protected $name = 'of';
+
+    /**
+     * Longer string name of this protocol class
+     *
+     * @type string
+     */
+    protected $name_long = "Open Fortress";
+}

+ 183 - 0
protocol/GameQ/Protocols/Openttd.php

@@ -0,0 +1,183 @@
+<?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;
+
+/**
+ * OpenTTD Protocol Class
+ *
+ * Handles processing Open Transport Tycoon Deluxe servers
+ *
+ * @package GameQ\Protocols
+ * @author Wilson Jesus <>
+ */
+class Openttd extends Protocol
+{
+    /**
+     * Array of packets we want to look up.
+     * Each key should correspond to a defined method in this or a parent class
+     *
+     * @type array
+     */
+    protected $packets = [
+        self::PACKET_ALL => "\x03\x00\x00",
+    ];
+
+    /**
+     * The query protocol used to make the call
+     *
+     * @type string
+     */
+    protected $protocol = 'openttd';
+
+    /**
+     * String name of this protocol class
+     *
+     * @type string
+     */
+    protected $name = 'openttd';
+
+    /**
+     * Longer string name of this protocol class
+     *
+     * @type string
+     */
+    protected $name_long = "Open Transport Tycoon Deluxe";
+
+    /**
+     * The client join link
+     *
+     * @type string
+     */
+    protected $join_link = null;
+
+    /**
+     * Normalize settings for this protocol
+     *
+     * @type array
+     */
+    protected $normalize = [
+        // General
+        'general' => [
+            // target       => source
+            'hostname'   => 'hostname',
+            'mapname'    => 'map',
+            'maxplayers' => 'max_clients',
+            'numplayers' => 'clients',
+            'password'   => 'password',
+            'dedicated' => 'dedicated',
+        ],
+    ];
+
+    /**
+     * Handle response from the server
+     *
+     * @return mixed
+     * @throws Exception
+     */
+    public function processResponse()
+    {
+        // Make a buffer
+        $buffer = new Buffer(implode('', $this->packets_response));
+
+        // Get the length of the packet
+        $packetLength = $buffer->getLength();
+
+        // Grab the header
+        $length = $buffer->readInt16();
+        //$type = $buffer->readInt8();
+        $buffer->skip(1); // Skip the "$type" as its not used in the code, and to comply with phpmd it cant be assigned and not used.
+
+        // Header
+        // Figure out which packet response this is
+        if ($packetLength != $length) {
+            throw new Exception(__METHOD__ . " response type '" . bin2hex($length) . "' is not valid");
+        }
+
+        return call_user_func_array([$this, 'processServerInfo'], [$buffer]);
+    }
+
+    /**
+     * Handle processing the server information
+     *
+     * @param Buffer $buffer
+     *
+     * @return array
+     */
+    protected function processServerInfo(Buffer $buffer)
+    {
+        // Set the result to a new result instance
+        $result = new Result();
+       
+        $protocol_version = $buffer->readInt8();
+        $result->add('protocol_version', $protocol_version);
+
+        switch ($protocol_version) {
+            case 4:
+                $num_grfs = $buffer->readInt8(); #number of grfs
+                $result->add('num_grfs', $num_grfs);
+                //$buffer->skip ($num_grfs * 20); #skip grfs id and md5 hash
+
+                for ($i=0; $i<$num_grfs; $i++) {
+                    $result->add('grfs_'.$i.'_ID', strtoupper(bin2hex($buffer->read(4))));
+                    $result->add('grfs_'.$i.'_MD5', strtoupper(bin2hex($buffer->read(16))));
+                }
+                // No break, cascades all the down even if case is meet
+            case 3:
+                $result->add('game_date', $buffer->readInt32());
+                $result->add('start_date', $buffer->readInt32());
+                // Cascades all the way down even if case is meet
+            case 2:
+                $result->add('companies_max', $buffer->readInt8());
+                $result->add('companies_on', $buffer->readInt8());
+                $result->add('spectators_max', $buffer->readInt8());
+                // Cascades all the way down even if case is meet
+            case 1:
+                $result->add('hostname', $buffer->readString());
+                $result->add('version', $buffer->readString());
+                
+                $language = $buffer->readInt8();
+                $result->add('language', $language);
+                $result->add('language_icon', '//media.openttd.org/images/server/'.$language.'_lang.gif');
+
+                $result->add('password', $buffer->readInt8());
+                $result->add('max_clients', $buffer->readInt8());
+                $result->add('clients', $buffer->readInt8());
+                $result->add('spectators', $buffer->readInt8());
+                if ($protocol_version < 3) {
+                    $days = ( 365 * 1920 + 1920 / 4 - 1920 / 100 + 1920 / 400 );
+                    $result->add('game_date', $buffer->readInt16() + $days);
+                    $result->add('start_date', $buffer->readInt16() + $days);
+                }
+                $result->add('map', $buffer->readString());
+                $result->add('map_width', $buffer->readInt16());
+                $result->add('map_height', $buffer->readInt16());
+                $result->add('map_type', $buffer->readInt8());
+                $result->add('dedicated', $buffer->readInt8());
+                // Cascades all the way down even if case is meet
+        }
+        unset($buffer);
+
+        return $result->fetch();
+    }
+}

+ 7 - 0
protocol/GameQ/Protocols/Pixark.php

@@ -40,4 +40,11 @@ class Pixark extends Arkse
      * @type string
      */
     protected $name_long = "PixARK";
+
+    /**
+     * Query port = client_port + 1
+     *
+     * @type int
+     */
+    protected $port_diff = 1;
 }

+ 50 - 0
protocol/GameQ/Protocols/Postscriptum.php

@@ -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 Postscriptum
+ *
+ * @package GameQ\Protocols
+ * @author  Austin Bischoff <[email protected]>
+ */
+class Postscriptum extends Source
+{
+    /**
+     * String name of this protocol class
+     *
+     * @type string
+     */
+    protected $name = 'postscriptum';
+
+    /**
+     * Longer string name of this protocol class
+     *
+     * @type string
+     */
+    protected $name_long = "Post Scriptum";
+
+    /**
+     * query_port = client_port + 10
+     * 64092 = 64090 + 10
+     *
+     * @type int
+     */
+    protected $port_diff = 10;
+}

+ 84 - 0
protocol/GameQ/Protocols/Quake4.php

@@ -0,0 +1,84 @@
+<?php
+/**
+ * This file is part of GameQ.
+ *
+ * GameQ is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GameQ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace GameQ\Protocols;
+
+use GameQ\Buffer;
+use GameQ\Result;
+
+/**
+ * Quake 4 Protocol Class
+ *
+ * @package GameQ\Protocols
+ *
+ * @author  Wilson Jesus <>
+ */
+class Quake4 extends Doom3
+{
+    /**
+     * String name of this protocol class
+     *
+     * @type string
+     */
+    protected $name = 'quake4';
+
+    /**
+     * Longer string name of this protocol class
+     *
+     * @type string
+     */
+    protected $name_long = "Quake 4";
+
+    /**
+     * Handle processing of player data
+     *
+     * @param \GameQ\Buffer $buffer
+     *
+     * @return array
+     */
+    protected function processPlayers(Buffer $buffer)
+    {
+        // Some games do not have a number of current players
+        $playerCount = 0;
+
+        // Set the result to a new result instance
+        $result = new Result();
+
+        // Parse players
+        // Loop thru the buffer until we run out of data
+        while (($id = $buffer->readInt8()) != 32) {
+            // Add player info results
+            $result->addPlayer('id', $id);
+            $result->addPlayer('ping', $buffer->readInt16());
+            $result->addPlayer('rate', $buffer->readInt32());
+            // Add player name, encoded
+            $result->addPlayer('name', utf8_encode(trim($buffer->readString())));
+            $result->addPlayer('clantag', $buffer->readString());
+            // Increment
+            $playerCount++;
+        }
+
+        // Add the number of players to the result
+        $result->add('numplayers', $playerCount);
+
+        // Clear
+        unset($buffer, $playerCount);
+
+        return $result->fetch();
+    }
+}

+ 166 - 0
protocol/GameQ/Protocols/Raknet.php

@@ -0,0 +1,166 @@
+<?php
+/**
+ * This file is part of GameQ.
+ *
+ * GameQ is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GameQ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace GameQ\Protocols;
+
+use GameQ\Buffer;
+use GameQ\Exception\Protocol as Exception;
+use GameQ\Protocol;
+use GameQ\Result;
+use GameQ\Server;
+
+/**
+ * Raknet Protocol Class
+ *
+ * See https://wiki.vg/Raknet_Protocol for more techinal information
+ *
+ * @author Austin Bischoff <[email protected]>
+ */
+class Raknet extends Protocol
+{
+    /**
+     * The magic string that is sent to get access to the server information
+     */
+    const OFFLINE_MESSAGE_DATA_ID = "\x00\xFF\xFF\x00\xFE\xFE\xFE\xFE\xFD\xFD\xFD\xFD\x12\x34\x56\x78";
+
+    /**
+     * Expected first part of the response from the server after query
+     */
+    const ID_UNCONNECTED_PONG = "\x1C";
+
+    /**
+     * Array of packets we want to look up.
+     * Each key should correspond to a defined method in this or a parent class
+     *
+     * @type array
+     */
+    protected $packets = [
+        self::PACKET_STATUS => "\x01%s%s\x02\x00\x00\x00\x00\x00\x00\x00", // Format time, magic,
+    ];
+
+    /**
+     * The query protocol used to make the call
+     *
+     * @type string
+     */
+    protected $protocol = 'raknet';
+
+    /**
+     * String name of this protocol class
+     *
+     * @type string
+     */
+    protected $name = 'raknet';
+
+    /**
+     * Longer string name of this protocol class
+     *
+     * @type string
+     */
+    protected $name_long = "Raknet Server";
+
+    /**
+     * Do some work to build the packet we need to send out to query
+     *
+     * @param Server $server
+     *
+     * @return void
+     */
+    public function beforeSend(Server $server)
+    {
+        // Update the server status packet before it is sent
+        $this->packets[self::PACKET_STATUS] = sprintf(
+            $this->packets[self::PACKET_STATUS],
+            pack('Q', time()),
+            self::OFFLINE_MESSAGE_DATA_ID
+        );
+    }
+
+    /**
+     * Process the response
+     *
+     * @return array
+     * @throws \GameQ\Exception\Protocol
+     */
+    public function processResponse()
+    {
+        // Merge the response array into a buffer. Unknown if this protocol does split packets or not
+        $buffer = new Buffer(implode($this->packets_response));
+
+        // Read first character from response. It should match below
+        $header = $buffer->read(1);
+
+        // Check first character to make sure the header matches
+        if ($header !== self::ID_UNCONNECTED_PONG) {
+            throw new Exception(sprintf(
+                '%s The header returned "%s" does not match the expected header of "%s"',
+                __METHOD__,
+                bin2hex($header),
+                bin2hex(self::ID_UNCONNECTED_PONG)
+            ));
+        }
+
+        // Burn the time section
+        $buffer->skip(8);
+
+        // Server GUID is next
+        $serverGUID = $buffer->readInt64();
+
+        // Read the next set to check to make sure the "magic" matches
+        $magicCheck = $buffer->read(16);
+
+        // Magic check fails
+        if ($magicCheck !== self::OFFLINE_MESSAGE_DATA_ID) {
+            throw new Exception(sprintf(
+                '%s The magic value returned "%s" does not match the expected value of "%s"',
+                __METHOD__,
+                bin2hex($magicCheck),
+                bin2hex(self::OFFLINE_MESSAGE_DATA_ID)
+            ));
+        }
+
+        // According to docs the next character is supposed to be used for a length and string for the following
+        // character for the MOTD but it appears to be implemented incorrectly
+        // Burn the next two characters instead of trying to do anything useful with them
+        $buffer->skip(2);
+
+        // Set the result to a new result instance
+        $result = new Result();
+
+        // Here on is server information delimited by semicolons (;)
+        $info = explode(';', $buffer->getBuffer());
+
+        $result->add('edition', $info[0]);
+        $result->add('motd_line_1', $info[1]);
+        $result->add('protocol_version', (int)$info[2]);
+        $result->add('version', $info[3]);
+        $result->add('num_players', (int)$info[4]);
+        $result->add('max_players', (int)$info[5]);
+        $result->add('server_uid', $info[6]);
+        $result->add('motd_line_2', $info[7]);
+        $result->add('gamemode', $info[8]);
+        $result->add('gamemode_numeric', (int)$info[9]);
+        $result->add('port_ipv4', (isset($info[10])) ? (int)$info[10] : null);
+        $result->add('port_ipv6', (isset($info[11])) ? (int)$info[11] : null);
+        $result->add('dedicated', 1);
+
+        unset($header, $serverGUID, $magicCheck, $info);
+
+        return $result->fetch();
+    }
+}

+ 50 - 0
protocol/GameQ/Protocols/Rf2.php

@@ -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 rFactor2
+ *
+ * @package GameQ\Protocols
+ * @author Wilson Jesus <>
+ */
+class Rf2 extends Source
+{
+    /**
+     * String name of this protocol class
+     *
+     * @type string
+     */
+    protected $name = 'rf2';
+
+    /**
+     * Longer string name of this protocol class
+     *
+     * @type string
+     */
+    protected $name_long = "rFactor 2";
+
+    /**
+     * query_port = client_port + 2
+     * 64092 = 64090 + 2
+     *
+     * @type int
+     */
+    protected $port_diff = 2;
+}

+ 43 - 0
protocol/GameQ/Protocols/Rfactor.php

@@ -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 rFactor
+ *
+ * @package GameQ\Protocols
+ * @author Austin Bischoff <[email protected]>
+ * @author Jesse Lukas <[email protected]>
+ */
+class Rfactor extends Source
+{
+    /**
+     * String name of this protocol class
+     *
+     * @type string
+     */
+    protected $name = 'rfactor';
+
+    /**
+     * Longer string name of this protocol class
+     *
+     * @type string
+     */
+    protected $name_long = "rFactor";
+}

+ 43 - 0
protocol/GameQ/Protocols/Rfactor2.php

@@ -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 rFactor2
+ *
+ * @package GameQ\Protocols
+ * @author Austin Bischoff <[email protected]>
+ * @author Jesse Lukas <[email protected]>
+ */
+class Rfactor2 extends Source
+{
+    /**
+     * String name of this protocol class
+     *
+     * @type string
+     */
+    protected $name = 'rfactor2';
+
+    /**
+     * Longer string name of this protocol class
+     *
+     * @type string
+     */
+    protected $name_long = "rFactor2";
+}

+ 21 - 0
protocol/GameQ/Protocols/Rust.php

@@ -18,6 +18,8 @@
 
 namespace GameQ\Protocols;
 
+use GameQ\Buffer;
+
 /**
  * Class Rust
  *
@@ -40,4 +42,23 @@ class Rust extends Source
      * @type string
      */
     protected $name_long = "Rust";
+    
+    /**
+     * Overload so we can get max players from mp of keywords and num players from cp keyword
+     *
+     * @param Buffer $buffer
+     */
+    protected function processDetails(Buffer $buffer)
+    {
+        $results = parent::processDetails($buffer);
+
+        if ($results['keywords']) {
+            //get max players from mp of keywords and num players from cp keyword
+            preg_match_all('/(mp|cp)([\d]+)/', $results['keywords'], $matches);
+            $results['max_players'] = intval($matches[2][0]);
+            $results['num_players'] = intval($matches[2][1]);
+        }
+
+        return $results;
+    }
 }

+ 50 - 0
protocol/GameQ/Protocols/Sco.php

@@ -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 Sven Co-op
+ *
+ * @package GameQ\Protocols
+ * @author Austin Bischoff <[email protected]>
+ * @author Jesse Lukas <[email protected]>
+ */
+class Sco extends Source
+{
+    /**
+     * Sven Co-op protocol class
+     *
+     * @type string
+     */
+    protected $name = 'sco';
+
+    /**
+     * Longer string name of this protocol class
+     *
+     * @type string
+     */
+    protected $name_long = "Sven Co-op";
+
+    /**
+     * query_port = client_port + 1
+     *
+     * @type int
+     */
+    protected $port_diff = 1;
+}

+ 1 - 1
protocol/GameQ/Protocols/Serioussam.php

@@ -23,7 +23,7 @@ namespace GameQ\Protocols;
  *
  * @author ZCaliptium <[email protected]>
  */
-class SeriousSam extends Gamespy
+class Serioussam extends Gamespy
 {
 
     /**

+ 2 - 2
protocol/GameQ/Protocols/Sevendaystodie.php

@@ -41,9 +41,9 @@ class Sevendaystodie extends Source
     protected $name_long = "7 Days to Die";
 
     /**
-     * query_port = client_port + 1
+     * query_port = client_port + 0
      *
      * @type int
      */
-    protected $port_diff = 1;
+    protected $port_diff = 0;
 }

+ 1 - 1
protocol/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",
     ];

+ 50 - 0
protocol/GameQ/Protocols/Stormworks.php

@@ -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 Stormworks
+ *
+ * @package GameQ\Protocols
+ * @author Austin Bischoff <[email protected]>
+ * @author Jesse Lukas <[email protected]>
+ */
+class Stormworks extends Source
+{
+    /**
+     * Stormworks protocol class
+     *
+     * @type string
+     */
+    protected $name = 'stormworks';
+
+    /**
+     * Longer string name of this protocol class
+     *
+     * @type string
+     */
+    protected $name_long = "Stormworks";
+
+    /**
+     * query_port = client_port + 1
+     *
+     * @type int
+     */
+    protected $port_diff = 1;
+}

+ 48 - 0
protocol/GameQ/Protocols/Valheim.php

@@ -0,0 +1,48 @@
+<?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;
+
+/**
+ * Valheim Protocol Class
+ *
+ * @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";
+
+    /**
+     * query_port = client_port + 1
+     *
+     * @type int
+     */
+    protected $port_diff = 1;
+}

+ 7 - 7
protocol/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;

+ 48 - 0
protocol/GameQ/Protocols/Vrising.php

@@ -0,0 +1,48 @@
+<?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;
+
+/**
+ * V Rining Protocol Class
+ *
+ * @package GameQ\Protocols
+ */
+class Vrising extends Source
+{
+    /**
+     * String name of this protocol class
+     *
+     * @type string
+     */
+    protected $name = 'vrising';
+
+    /**
+     * Longer string name of this protocol class
+     *
+     * @type string
+     */
+    protected $name_long = "V Rising";
+
+    /**
+     * query_port = client_port + 1
+     *
+     * @type int
+     */
+    protected $port_diff = 1;
+}

+ 42 - 0
protocol/GameQ/Protocols/Zomboid.php

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

+ 25 - 0
protocol/GameQ/Query/Core.php

@@ -119,6 +119,31 @@ abstract class Core
         $this->blocking = false;
     }
 
+    public function getTransport()
+    {
+        return $this->transport;
+    }
+
+    public function getIp()
+    {
+        return $this->ip;
+    }
+
+    public function getPort()
+    {
+        return $this->port;
+    }
+
+    public function getTimeout()
+    {
+        return $this->timeout;
+    }
+
+    public function getBlocking()
+    {
+        return $this->blocking;
+    }
+
     /**
      * Create a new socket
      *

+ 7 - 1
protocol/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.
                 }
 

+ 16 - 7
protocol/GameQ/Server.php

@@ -27,7 +27,6 @@ use GameQ\Exception\Server as Exception;
  */
 class Server
 {
-
     /*
      * Server array keys
      */
@@ -203,13 +202,18 @@ class Server
                 );
             }
 
-            // Validate the IPv4 value, if FALSE is not a valid IP, maybe a hostname.  Try to resolve
-            if (!filter_var($this->ip, FILTER_VALIDATE_IP, ['flags' => FILTER_FLAG_IPV4,])
-                && $this->ip === gethostbyname($this->ip)
-            ) {
+            // Validate the IPv4 value, if FALSE is not a valid IP, maybe a hostname.
+            if (! filter_var($this->ip, FILTER_VALIDATE_IP, ['flags' => FILTER_FLAG_IPV4,])) {
+                // Try to resolve the hostname to IPv4
+                $resolved = gethostbyname($this->ip);
+
                 // When gethostbyname() fails it returns the original string
-                // so if ip and the result from gethostbyname() are equal this failed.
-                throw new Exception("Unable to resolve the host '{$this->ip}' to an IP address.");
+                if ($this->ip === $resolved) {
+                    // so if ip and the result from gethostbyname() are equal this failed.
+                    throw new Exception("Unable to resolve the host '{$this->ip}' to an IP address.");
+                } else {
+                    $this->ip = $resolved;
+                }
             }
         }
     }
@@ -258,6 +262,11 @@ class Server
         return (array_key_exists($key, $this->options)) ? $this->options[$key] : null;
     }
 
+    public function getOptions()
+    {
+        return $this->options;
+    }
+
     /**
      * Get the ID for this server
      *