diff --git a/src/GameQ/Protocols/Arma3.php b/src/GameQ/Protocols/Arma3.php new file mode 100644 index 00000000..6ea46979 --- /dev/null +++ b/src/GameQ/Protocols/Arma3.php @@ -0,0 +1,183 @@ +. + */ + +namespace GameQ\Protocols; + +use GameQ\Buffer; +use GameQ\Result; + +/** + * Class Armed Assault 3 + * + * Rules protocol reference: https://community.bistudio.com/wiki/Arma_3_ServerBrowserProtocol2 + * + * @package GameQ\Protocols + * @author Austin Bischoff + * @author Memphis017 + */ +class Arma3 extends Source +{ + /** + * DLC ^2 constants + */ + const DLC_KARTS = 1, + DLC_MARKSMEN = 2, + DLC_HELICOPTERS = 4, + DLC_APEX = 16; + + /** + * Defines the names for the specific game DLCs + * + * @var array + */ + protected $dlcNames = [ + self::DLC_KARTS => 'Karts', + self::DLC_MARKSMEN => 'Marksmen', + self::DLC_HELICOPTERS => 'Helicopters', + self::DLC_APEX => 'Apex', + ]; + + + /** + * String name of this protocol class + * + * @type string + */ + protected $name = 'arma3'; + + /** + * Longer string name of this protocol class + * + * @type string + */ + protected $name_long = "Arma3"; + + /** + * Query port = client_port + 1 + * + * @type int + */ + protected $port_diff = 1; + + /** + * Process the rules since Arma3 changed their response for rules + * + * @param Buffer $buffer + * + * @return array + * @throws \GameQ\Exception\Protocol + */ + protected function processRules(Buffer $buffer) + { + // Total number of packets, burn it + $buffer->readInt16(); + + // Will hold the data string + $data = ''; + + // Loop until we run out of strings + while ($buffer->getLength()) { + // Burn the delimiters (i.e. \x01\x04\x00) + $buffer->readString(); + + // Add the data to the string, we are reassembling it + $data .= $buffer->readString(); + } + + // Restore escaped sequences + $data = str_replace(["\x01\x01", "\x01\x02", "\x01\x03"], ["\x01", "\x00", "\xFF"], $data); + + // Make a new buffer with the reassembled data + $responseBuffer = new Buffer($data); + + // Kill the old buffer, should be empty + unset($buffer, $data); + + // Set the result to a new result instance + $result = new Result(); + + // Get results + $result->add('rules_protocol_version', $responseBuffer->readInt8()); + $result->add('overflow', $responseBuffer->readInt8()); + $dlcBit = $responseBuffer->readInt8(); // Grab DLC bit and use it later + $responseBuffer->skip(); // Reserved, burn it + // Grab difficulty so we can man handle it... + $difficulty = $responseBuffer->readInt8(); + + // Process difficulty + $result->add('3rd_person', $difficulty >> 7); + $result->add('advanced_flight_mode', ($difficulty >> 6) & 1); + $result->add('difficulty_ai', ($difficulty >> 3) & 3); + $result->add('difficulty_level', $difficulty & 3); + + unset($difficulty); + + // Crosshair + $result->add('crosshair', $responseBuffer->readInt8()); + + // Next are the dlc bits so loop over the dlcBit so we can determine which DLC's are running + for ($x = 1; $x <= $dlcBit; $x *= 2) { + // Enabled, add it to the list + if ($x & $dlcBit) { + $result->addSub('dlcs', 'name', $this->dlcNames[$x]); + $result->addSub('dlcs', 'hash', dechex($responseBuffer->readInt32())); + } + } + + // No longer needed + unset($dlcBit); + + // Grab the mod count + $modCount = $responseBuffer->readInt8(); + + // Add mod count + $result->add('mod_count', $modCount); + + // Loop the mod count and add them + for ($x = 0; $x < $modCount; $x++) { + // Add the mod to the list + $result->addSub('mods', 'hash', dechex($responseBuffer->readInt32())); + $result->addSub('mods', 'steam_id', hexdec($responseBuffer->readPascalString(0, true))); + $result->addSub('mods', 'name', $responseBuffer->readPascalString(0, true)); + } + + unset($modCount, $x); + + // Burn the signatures count, we will just loop until we run out of strings + $responseBuffer->read(); + + // Make signatures array + $signatures = []; + + // Loop until we run out of strings + while ($responseBuffer->getLength()) { + //$result->addSub('signatures', 0, $responseBuffer->readPascalString(0, true)); + $signatures[] = $responseBuffer->readPascalString(0, true); + } + + // Add as a simple array + $result->add('signatures', $signatures); + + // Add signatures count + $result->add('signature_count', count($signatures)); + + unset($responseBuffer, $signatures); + + return $result->fetch(); + } +} diff --git a/src/GameQ/Protocols/Armedassault3.php b/src/GameQ/Protocols/Armedassault3.php index 1d938f81..5bbca429 100644 --- a/src/GameQ/Protocols/Armedassault3.php +++ b/src/GameQ/Protocols/Armedassault3.php @@ -19,31 +19,14 @@ namespace GameQ\Protocols; /** - * Class Armed Assault 3 + * Armed assault 3 dummy Protocol Class * - * @package GameQ\Protocols - * @author Austin Bischoff + * Added for backward compatibility, please update to class arma3 + * + * @deprecated v3.0.10 + * @package GameQ\Protocols + * @author Austin Bischoff */ -class Armedassault3 extends Source +class Armedassault3 extends Arma3 { - /** - * String name of this protocol class - * - * @type string - */ - protected $name = 'armedassault3'; - - /** - * Longer string name of this protocol class - * - * @type string - */ - protected $name_long = "Armed Assault 3"; - - /** - * Query port = client_port + 1 - * - * @type int - */ - protected $port_diff = 1; } diff --git a/tests/Protocols/Armedassault3.php b/tests/Protocols/Arma3.php similarity index 95% rename from tests/Protocols/Armedassault3.php rename to tests/Protocols/Arma3.php index 4ac2ed49..35118a89 100644 --- a/tests/Protocols/Armedassault3.php +++ b/tests/Protocols/Arma3.php @@ -23,7 +23,7 @@ * * @package GameQ\Tests\Protocols */ -class Armedassault3 extends Base +class Arma3 extends Base { /** * Test responses for Arma 3 @@ -41,7 +41,7 @@ public function testResponses($responses, $result) $testResult = $this->queryTest( $server, - 'armedassault3', + 'arma3', $responses ); diff --git a/tests/Protocols/Providers/Arma3/1_response.txt b/tests/Protocols/Providers/Arma3/1_response.txt new file mode 100644 index 00000000..e2d18485 Binary files /dev/null and b/tests/Protocols/Providers/Arma3/1_response.txt differ diff --git a/tests/Protocols/Providers/Arma3/1_result.json b/tests/Protocols/Providers/Arma3/1_result.json new file mode 100644 index 00000000..ad6dfd58 --- /dev/null +++ b/tests/Protocols/Providers/Arma3/1_result.json @@ -0,0 +1 @@ +{"192.223.29.245:2312":{"3rd_person":1,"advanced_flight_mode":0,"crosshair":1,"dedicated":"d","difficulty_ai":1,"difficulty_level":1,"dlcs":[{"name":"Karts","hash":"a5c8c24c"},{"name":"Marksmen","hash":"58644447"},{"name":"Helicopters","hash":"6b140d2c"},{"name":"Apex","hash":"c794d30d"}],"game_descr":"King of the Hill by Sa-Matra (v9+++, Infantry)","game_dir":"Arma3","game_id":107410,"gq_address":"192.223.29.245","gq_joinlink":"steam:\/\/connect\/192.223.29.245:2312\/","gq_name":"Arma3","gq_online":true,"gq_port_client":2312,"gq_port_query":2313,"gq_protocol":"source","gq_transport":"udp","gq_type":"arma3","hostname":"HostileTakeover.co - King Of The Hill - US #2 - Infantry","keywords":"bt,r166,n0,s7,i1,mf,lf,vt,dt,tkoth,g65545,h6ecdda65,c0-52,f0,pw,e15,j0,k0,","map":"Altis","max_players":120,"mod_count":0,"num_bots":0,"num_players":78,"os":"w","overflow":0,"password":0,"players":[{"id":0,"name":"t-rev","score":4,"time":1678.1127929688},{"id":0,"name":"gutpile","score":0,"time":1678.0617675781},{"id":0,"name":"Gamble","score":-3,"time":1678.0107421875},{"id":0,"name":"Abraxas","score":0,"time":1677.9584960938},{"id":0,"name":"OVERED","score":0,"time":1677.9068603516},{"id":0,"name":"AnimalMother","score":5,"time":1677.8547363281},{"id":0,"name":"JJbadtower","score":0,"time":1677.802734375},{"id":0,"name":"paulo","score":2,"time":1677.3776855469},{"id":0,"name":"AnimalBrother","score":1,"time":1669.1322021484},{"id":0,"name":"Easy","score":2,"time":1664.2368164062},{"id":0,"name":"ForbiddenLegend","score":2,"time":1663.6546630859},{"id":0,"name":"[FBA] Raphael","score":-5,"time":1662.3812255859},{"id":0,"name":"Vex","score":1,"time":1658.921875},{"id":0,"name":"RiK","score":2,"time":1651.7073974609},{"id":0,"name":"Nick","score":3,"time":1639.5153808594},{"id":0,"name":"ComandanteBoss","score":0,"time":1619.7233886719},{"id":0,"name":"Saintzinho","score":3,"time":1604.6950683594},{"id":0,"name":"Mushyyn","score":1,"time":1599.7711181641},{"id":0,"name":"Stalin","score":8,"time":1574.0270996094},{"id":0,"name":"Scarlatti","score":3,"time":1542.2535400391},{"id":0,"name":"oF13RCE","score":0,"time":1519.8354492188},{"id":0,"name":"Boyd Guy Manson","score":11,"time":1519.7712402344},{"id":0,"name":"El","score":2,"time":1477.3687744141},{"id":0,"name":"Ragnald","score":0,"time":1468.6982421875},{"id":0,"name":"Socalfisher","score":-2,"time":1466.2082519531},{"id":0,"name":"BINGHAM","score":-12,"time":1428.2354736328},{"id":0,"name":"Sgt. GhostRider","score":-2,"time":1414.9805908203},{"id":0,"name":"Buck McKenzie [SaltTeam6]","score":1,"time":1401.1704101562},{"id":0,"name":"Snu","score":0,"time":1381.2198486328},{"id":0,"name":"KATIBERO","score":-1,"time":1347.7222900391},{"id":0,"name":"SPEAR | Ratel [SPEAR]","score":0,"time":1336.0582275391},{"id":0,"name":"Edgar","score":0,"time":1291.2678222656},{"id":0,"name":"OLDSPICE [TLL]","score":0,"time":1172.7967529297},{"id":0,"name":"MasterJeffe","score":0,"time":1072.9832763672},{"id":0,"name":"Gen.Slayer","score":2,"time":1059.16796875},{"id":0,"name":"Brennan","score":0,"time":1023.8662719727},{"id":0,"name":"Joey","score":3,"time":997.58587646484},{"id":0,"name":"I | George King","score":9,"time":962.63208007812},{"id":0,"name":"KID","score":-5,"time":931.37731933594},{"id":0,"name":"Ethan","score":-1,"time":901.58911132812},{"id":0,"name":"marti","score":0,"time":821.86749267578},{"id":0,"name":"kingo","score":-6,"time":811.40942382812},{"id":0,"name":"InvisibleKiller","score":-2,"time":803.3681640625},{"id":0,"name":"Dennis","score":0,"time":788.11627197266},{"id":0,"name":"Hollow","score":0,"time":729.32281494141},{"id":0,"name":"[A.H] Twiistaskeey","score":1,"time":704.02703857422},{"id":0,"name":"Margret","score":0,"time":700.07385253906},{"id":0,"name":"Jeezus","score":0,"time":691.99047851562},{"id":0,"name":"ghostly_ninja1","score":-2,"time":684.95770263672},{"id":0,"name":"Major Drunk","score":1,"time":481.56610107422},{"id":0,"name":"vantak ","score":0,"time":473.2067565918},{"id":0,"name":"StEvE","score":0,"time":452.69955444336},{"id":0,"name":"Blastburn94","score":0,"time":443.25198364258},{"id":0,"name":"Bonerfart","score":0,"time":353.39660644531},{"id":0,"name":"TinkTink88","score":0,"time":351.58361816406},{"id":0,"name":"Lccoo","score":0,"time":349.22796630859},{"id":0,"name":"issac","score":1,"time":336.12591552734},{"id":0,"name":"[GFKR]Cameron","score":0,"time":296.54791259766},{"id":0,"name":"John","score":0,"time":290.63470458984},{"id":0,"name":"CPL. Noah","score":0,"time":278.65484619141},{"id":0,"name":"GloJudge [FMT]","score":0,"time":247.36380004883},{"id":0,"name":"borges","score":0,"time":225.27493286133},{"id":0,"name":"Glen","score":-2,"time":207.66102600098},{"id":0,"name":"Valentino Vegas","score":0,"time":201.99870300293},{"id":0,"name":"Super Critical","score":-13,"time":164.65266418457},{"id":0,"name":"Ryan","score":0,"time":148.58499145508},{"id":0,"name":"[666]Pr0Magic","score":0,"time":142.20388793945},{"id":0,"name":"Greys","score":0,"time":139.02325439453},{"id":0,"name":"Lilkbob","score":0,"time":118.11871337891},{"id":0,"name":"JarEp","score":0,"time":106.53130340576},{"id":0,"name":"BarMan","score":0,"time":102.55344390869},{"id":0,"name":"Donut","score":0,"time":98.713569641113},{"id":0,"name":"Lem","score":0,"time":98.636184692383},{"id":0,"name":"Tony Soprano","score":0,"time":83.17081451416},{"id":0,"name":"Michael Stevens","score":0,"time":47.136447906494},{"id":0,"name":"Bal'to","score":0,"time":21.11442565918},{"id":0,"name":"LiquidMetal","score":0,"time":7.8195986747742},{"id":0,"name":"Rando","score":0,"time":5.9321384429932}],"port":2312,"protocol":17,"rules_protocol_version":2,"secure":0,"signature_count":5,"signatures":["a3","DragonFyre25","dragonfyrelitev3","jsrs-apex","jsrs21"],"steam_id":90105598934646785,"steamappid":0,"version":"1.66.139519"}} \ No newline at end of file diff --git a/tests/Protocols/Providers/Arma3/2_response.txt b/tests/Protocols/Providers/Arma3/2_response.txt new file mode 100644 index 00000000..4646885b Binary files /dev/null and b/tests/Protocols/Providers/Arma3/2_response.txt differ diff --git a/tests/Protocols/Providers/Arma3/2_result.json b/tests/Protocols/Providers/Arma3/2_result.json new file mode 100644 index 00000000..674a5596 --- /dev/null +++ b/tests/Protocols/Providers/Arma3/2_result.json @@ -0,0 +1 @@ +{"172.93.107.250:2302":{"3rd_person":1,"advanced_flight_mode":0,"crosshair":1,"dedicated":"d","difficulty_ai":2,"difficulty_level":3,"dlcs":[{"name":"Karts","hash":"a5c8c24c"},{"name":"Marksmen","hash":"58644447"},{"name":"Helicopters","hash":"6b140d2c"},{"name":"Apex","hash":"c794d30d"}],"game_descr":"Altis Life ASYLUM","game_dir":"Arma3","game_id":107410,"gq_address":"172.93.107.250","gq_joinlink":"steam:\/\/connect\/172.93.107.250:2302\/","gq_name":"Arma3","gq_online":true,"gq_port_client":2302,"gq_port_query":2303,"gq_protocol":"source","gq_transport":"udp","gq_type":"arma3","hostname":"Asylum #3 : Altis Life | HOUSING | PERM GANGS | TERRITORY | Vig","keywords":"bt,r166,n0,s7,i3,mf,lf,vt,dt,trpg,g65545,h7d468d21,c97-33,f0,pw,e15,j0,k0,","map":"Altis","max_players":100,"mod_count":3,"mods":[{"hash":"0","steam_id":0,"name":"@extDB"},{"hash":"c2a22c98","steam_id":0,"name":"@life_server"},{"hash":"0","steam_id":0,"name":"@Arma2NET"}],"num_bots":0,"num_players":95,"os":"w","overflow":0,"password":0,"players":[{"id":0,"name":"[FSA] Qarrad Shekau","score":0,"time":1263.1661376953},{"id":0,"name":"Nicholas","score":0,"time":1243.7283935547},{"id":0,"name":"Kelly ","score":0,"time":1234.6563720703},{"id":0,"name":"[FSA] Tr'ap Al Razi [FSA]","score":-1,"time":1228.1246337891},{"id":0,"name":"も Pippi","score":0,"time":1208.3363037109},{"id":0,"name":"DaX","score":-1,"time":1205.6791992188},{"id":0,"name":"[FSA] Andr'Ew Spazad","score":0,"time":1203.8122558594},{"id":0,"name":"も ЯoguePilot","score":0,"time":1194.1365966797},{"id":0,"name":"Sjuol","score":0,"time":1192.2985839844},{"id":0,"name":"≈BnH≈ BushWookie","score":0,"time":1186.6676025391},{"id":0,"name":"[FSA] Jihadi Jim","score":0,"time":1162.0375976562},{"id":0,"name":"す OJ","score":0,"time":1162.0068359375},{"id":0,"name":"The Weeb™","score":0,"time":1161.7027587891},{"id":0,"name":"[FSA] Jihadi John","score":0,"time":1149.2513427734},{"id":0,"name":"も OLL13","score":-1,"time":1149.2008056641},{"id":0,"name":"Chorizo","score":0,"time":1144.2586669922},{"id":0,"name":"も bunni","score":-2,"time":1136.2474365234},{"id":0,"name":"-ア- Sammy:)","score":0,"time":1132.0949707031},{"id":0,"name":"O'Brien","score":0,"time":1131.9544677734},{"id":0,"name":"Country Bob","score":0,"time":1131.9245605469},{"id":0,"name":"す Windmere","score":0,"time":1127.7075195312},{"id":0,"name":"Anthony","score":0,"time":1115.5446777344},{"id":0,"name":"Bustin","score":0,"time":1102.7288818359},{"id":0,"name":"-ア- A N D R E W ^ F A M","score":0,"time":1097.8731689453},{"id":0,"name":"LCpl. J. Bradley","score":0,"time":1093.2923583984},{"id":0,"name":"も Rogue and the boys","score":-1,"time":1091.4332275391},{"id":0,"name":"す Guy","score":0,"time":1081.2150878906},{"id":0,"name":"[FSA] Jihadi Zoghbi","score":0,"time":1080.4140625},{"id":0,"name":"[FSA] Aleec Spazeed","score":0,"time":1079.8717041016},{"id":0,"name":"-ア- tiger","score":-1,"time":1073.5656738281},{"id":0,"name":"Tony","score":0,"time":1066.3386230469},{"id":0,"name":"[CoRe] beirut","score":0,"time":1059.1934814453},{"id":0,"name":"-ア- Reformed Jay","score":-1,"time":1051.4249267578},{"id":0,"name":"-ア- Schiang","score":-4,"time":1049.0574951172},{"id":0,"name":"HomeUser","score":0,"time":1048.6882324219},{"id":0,"name":"«Pß» Willi","score":-1,"time":1029.9713134766},{"id":0,"name":"も Ranga","score":0,"time":1022.6834106445},{"id":0,"name":"Cole","score":-1,"time":1022.5681152344},{"id":0,"name":"[FSA] Cawkem Spazood","score":0,"time":1008.9495239258},{"id":0,"name":"[‡] DISEASED","score":0,"time":987.65960693359},{"id":0,"name":"-ア- Millar","score":0,"time":958.96734619141},{"id":0,"name":"Taylor Swift","score":0,"time":955.25830078125},{"id":0,"name":"も Huskers the nurse","score":0,"time":940.98559570312},{"id":0,"name":"ViiET","score":0,"time":865.966796875},{"id":0,"name":"も HYCH","score":0,"time":865.90582275391},{"id":0,"name":"も ASSERT","score":0,"time":859.52777099609},{"id":0,"name":"Dv | Kazem Al-saher [THUGLIFE]","score":0,"time":854.16741943359},{"id":0,"name":"т | Apci :)","score":0,"time":834.517578125},{"id":0,"name":"meow","score":0,"time":833.96429443359},{"id":0,"name":"BlackShot","score":0,"time":819.013671875},{"id":0,"name":"Matt","score":0,"time":812.22778320312},{"id":0,"name":"[FSA] Envy Spazeed","score":0,"time":808.06927490234},{"id":0,"name":";)))))","score":0,"time":802.28863525391},{"id":0,"name":"も Doctor Who","score":0,"time":792.32836914062},{"id":0,"name":"SGT. Hugo stiglitz","score":0,"time":788.31060791016},{"id":0,"name":"Fooberz","score":0,"time":765.57580566406},{"id":0,"name":"Draleken","score":0,"time":738.80889892578},{"id":0,"name":"Schwider","score":0,"time":732.14337158203},{"id":0,"name":"fatca","score":0,"time":730.53979492188},{"id":0,"name":"Dxbstep","score":0,"time":728.11932373047},{"id":0,"name":"Jax","score":0,"time":727.28112792969},{"id":0,"name":"Doctor","score":0,"time":709.26330566406},{"id":0,"name":"ßoats | JoM","score":0,"time":682.88397216797},{"id":0,"name":"emanuelmitrone","score":0,"time":670.7333984375},{"id":0,"name":"[FSA] Rashaad al Assad","score":0,"time":667.23211669922},{"id":0,"name":"AS