-
-
Notifications
You must be signed in to change notification settings - Fork 138
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added quake 2 base protocol with tests. Fixes #343.
- Loading branch information
Showing
9 changed files
with
371 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
<?php | ||
|
||
|
||
namespace GameQ\Protocols; | ||
|
||
use GameQ\Protocol; | ||
use GameQ\Buffer; | ||
use GameQ\Result; | ||
use GameQ\Exception\Protocol as Exception; | ||
|
||
/** | ||
* Quake2 Protocol Class | ||
* | ||
* Handles processing Quake 3 servers | ||
* | ||
* @package GameQ\Protocols | ||
*/ | ||
class Quake2 extends Protocol | ||
{ | ||
/** | ||
* Array of packets we want to look up. | ||
* Each key should correspond to a defined method in this or a parent class | ||
* | ||
* @type array | ||
*/ | ||
protected $packets = [ | ||
self::PACKET_STATUS => "\xFF\xFF\xFF\xFFstatus\x00", | ||
]; | ||
|
||
/** | ||
* Use the response flag to figure out what method to run | ||
* | ||
* @type array | ||
*/ | ||
protected $responses = [ | ||
"\xFF\xFF\xFF\xFF\x70\x72\x69\x6e\x74" => 'processStatus', | ||
]; | ||
|
||
/** | ||
* The query protocol used to make the call | ||
* | ||
* @type string | ||
*/ | ||
protected $protocol = 'quake2'; | ||
|
||
/** | ||
* String name of this protocol class | ||
* | ||
* @type string | ||
*/ | ||
protected $name = 'quake2'; | ||
|
||
/** | ||
* Longer string name of this protocol class | ||
* | ||
* @type string | ||
*/ | ||
protected $name_long = "Quake 2 Server"; | ||
|
||
/** | ||
* The client join link | ||
* | ||
* @type string | ||
*/ | ||
protected $join_link = null; | ||
|
||
/** | ||
* Normalize settings for this protocol | ||
* | ||
* @type array | ||
*/ | ||
protected $normalize = [ | ||
// General | ||
'general' => [ | ||
// target => source | ||
'gametype' => 'gamename', | ||
'hostname' => 'hostname', | ||
'mapname' => 'mapname', | ||
'maxplayers' => 'maxclients', | ||
'mod' => 'g_gametype', | ||
'numplayers' => 'clients', | ||
'password' => 'password', | ||
], | ||
// Individual | ||
'player' => [ | ||
'name' => 'name', | ||
'ping' => 'ping', | ||
'score' => 'frags', | ||
], | ||
]; | ||
|
||
/** | ||
* Handle response from the server | ||
* | ||
* @return mixed | ||
* @throws Exception | ||
*/ | ||
public function processResponse() | ||
{ | ||
// Make a buffer | ||
$buffer = new Buffer(implode('', $this->packets_response)); | ||
|
||
// Grab the header | ||
$header = $buffer->readString("\x0A"); | ||
|
||
// Figure out which packet response this is | ||
if (empty($header) || !array_key_exists($header, $this->responses)) { | ||
throw new Exception(__METHOD__ . " response type '" . bin2hex($header) . "' is not valid"); | ||
} | ||
|
||
return call_user_func_array([$this, $this->responses[$header]], [$buffer]); | ||
} | ||
|
||
/** | ||
* Process the status response | ||
* | ||
* @param Buffer $buffer | ||
* | ||
* @return array | ||
*/ | ||
protected function processStatus(Buffer $buffer) | ||
{ | ||
// We need to split the data and offload | ||
$results = $this->processServerInfo(new Buffer($buffer->readString("\x0A"))); | ||
|
||
$results = array_merge_recursive( | ||
$results, | ||
$this->processPlayers(new Buffer($buffer->getBuffer())) | ||
); | ||
|
||
unset($buffer); | ||
|
||
// Return results | ||
return $results; | ||
} | ||
|
||
/** | ||
* Handle processing the server information | ||
* | ||
* @param Buffer $buffer | ||
* | ||
* @return array | ||
*/ | ||
protected function processServerInfo(Buffer $buffer) | ||
{ | ||
// Set the result to a new result instance | ||
$result = new Result(); | ||
|
||
// Burn leading \ if one exists | ||
$buffer->readString('\\'); | ||
|
||
// Key / value pairs | ||
while ($buffer->getLength()) { | ||
// Add result | ||
$result->add( | ||
trim($buffer->readString('\\')), | ||
utf8_encode(trim($buffer->readStringMulti(['\\', "\x0a"]))) | ||
); | ||
} | ||
|
||
$result->add('password', 0); | ||
$result->add('mod', 0); | ||
|
||
unset($buffer); | ||
|
||
return $result->fetch(); | ||
} | ||
|
||
/** | ||
* Handle processing of player data | ||
* | ||
* @param Buffer $buffer | ||
* | ||
* @return array | ||
*/ | ||
protected function processPlayers(Buffer $buffer) | ||
{ | ||
// Some games do not have a number of current players | ||
$playerCount = 0; | ||
|
||
// Set the result to a new result instance | ||
$result = new Result(); | ||
|
||
// Loop until we are out of data | ||
while ($buffer->getLength()) { | ||
// Make a new buffer with this block | ||
$playerInfo = new Buffer($buffer->readString("\x0A")); | ||
|
||
// Add player info | ||
$result->addPlayer('frags', $playerInfo->readString("\x20")); | ||
$result->addPlayer('ping', $playerInfo->readString("\x20")); | ||
|
||
// Skip first " | ||
$playerInfo->skip(1); | ||
|
||
// Add player name, encoded | ||
$result->addPlayer('name', utf8_encode(trim(($playerInfo->readString('"'))))); | ||
|
||
// Skip first " | ||
$playerInfo->skip(2); | ||
|
||
// Add address | ||
$result->addPlayer('address', trim($playerInfo->readString('"'))); | ||
|
||
// Increment | ||
$playerCount++; | ||
|
||
// Clear | ||
unset($playerInfo); | ||
} | ||
|
||
$result->add('clients', $playerCount); | ||
|
||
// Clear | ||
unset($buffer, $playerCount); | ||
|
||
return $result->fetch(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
\Q2Admin\1.17.44-tsmod-2\mapname\q2dm6\anticheat\1\sv_noobquad\0\gamedate\Aug 2 2009\gamename\baseq2\sv_handicap\1\maxspectators\8\INFO2\NO BOTS, HACKS, CHEATS PLEASE\INFO1\All Skill Levels Welcome\cheats\0\timelimit\15\fraglimit\30\dmflags\16404\deathmatch\1\version\R1Q2 b7864 i386 Oct 1 2008 Linux\hostname\tastyspleen.net::dm\maxclients\17 | ||
0 2 "WallFly[BZZZ]" | ||
2 162 "ninja" | ||
3 196 "Richo de Lula" | ||
22 52 "Fatal[KiD]" | ||
15 25 "Anus Paste" | ||
12 22 "hindmost" | ||
29 59 "[A2W]H3mr0yd" | ||
4 159 "nx0" | ||
26 40 "0:0" | ||
18 41 "mag" | ||
9 71 "mind" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{"67.228.69.114:27916":{"INFO1":"All Skill Levels Welcome","INFO2":"NO BOTS, HACKS, CHEATS PLEASE","Q2Admin":"1.17.44-tsmod-2","anticheat":"1","cheats":"0","clients":11,"deathmatch":"1","dmflags":"16404","fraglimit":"30","gamedate":"Aug 2 2009","gamename":"baseq2","gq_address":"67.228.69.114","gq_joinlink":"","gq_name":"Quake 2 Server","gq_online":true,"gq_port_client":27916,"gq_port_query":27916,"gq_protocol":"quake2","gq_transport":"udp","gq_type":"quake2","hostname":"tastyspleen.net::dm","mapname":"q2dm6","maxclients":"17","maxspectators":"8","mod":0,"password":0,"players":[{"frags":"0","ping":"2","name":"WallFly[BZZZ]","address":""},{"frags":"2","ping":"162","name":"ninja","address":""},{"frags":"3","ping":"196","name":"Richo de Lula","address":""},{"frags":"22","ping":"52","name":"Fatal[KiD]","address":""},{"frags":"15","ping":"25","name":"Anus Paste","address":""},{"frags":"12","ping":"22","name":"hindmost","address":""},{"frags":"29","ping":"59","name":"[A2W]H3mr0yd","address":""},{"frags":"4","ping":"159","name":"nx0","address":""},{"frags":"26","ping":"40","name":"0:0","address":""},{"frags":"18","ping":"41","name":"mag","address":""},{"frags":"9","ping":"71","name":"mind","address":""}],"sv_handicap":"1","sv_noobquad":"0","timelimit":"15","version":"R1Q2 b7864 i386 Oct 1 2008 Linux"}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
\mapname\q2dm1\ngWorldStats_Status\<<< Disabled >>>\anticheat\1\match_info\Runes, Hook, Protection\match_type\RegularDM\time_remaining\17:28\gamedate\Jul 1 1999\gamename\baseq2 \cheats\0\timelimit\20\fraglimit\0\dmflags\17940\version\R1Q2 b7864 i386 Oct 1 2008 Linux\hostname\Clanworld's Lithium-DM #1\deathmatch\1\gamedir\ospl2\port\27910\game\ospl2\maxclients\32 | ||
0 15 "nikon" | ||
0 16 "Ulrik" | ||
0 16 "bubbleuniverse" | ||
0 18 "Centriot" | ||
0 17 "AL1AS" | ||
0 17 "Oilver" | ||
0 17 "snappy" | ||
16 55 "4833" | ||
0 17 "rackor" | ||
0 66 "Player" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{"77.66.46.219:27910":{"anticheat":"1","cheats":"0","clients":10,"deathmatch":"1","dmflags":"17940","fraglimit":"0","game":"ospl2","gamedate":"Jul 1 1999","gamedir":"ospl2","gamename":"baseq2","gq_address":"77.66.46.219","gq_joinlink":"","gq_name":"Quake 2 Server","gq_online":true,"gq_port_client":27910,"gq_port_query":27910,"gq_protocol":"quake2","gq_transport":"udp","gq_type":"quake2","hostname":"Clanworld's Lithium-DM #1","mapname":"q2dm1","match_info":"Runes, Hook, Protection","match_type":"RegularDM","maxclients":"32","mod":0,"ngWorldStats_Status":"<<< Disabled >>>","password":0,"players":[{"frags":"0","ping":"15","name":"nikon","address":""},{"frags":"0","ping":"16","name":"Ulrik","address":""},{"frags":"0","ping":"16","name":"bubbleuniverse","address":""},{"frags":"0","ping":"18","name":"Centriot","address":""},{"frags":"0","ping":"17","name":"AL1AS","address":""},{"frags":"0","ping":"17","name":"Oilver","address":""},{"frags":"0","ping":"17","name":"snappy","address":""},{"frags":"16","ping":"55","name":"4833","address":""},{"frags":"0","ping":"17","name":"rackor","address":""},{"frags":"0","ping":"66","name":"Player","address":""}],"port":"27910","time_remaining":"17:28","timelimit":"20","version":"R1Q2 b7864 i386 Oct 1 2008 Linux"}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
\Score_A\WARMUP\Score_B\WARMUP\anticheat\1\cheats\0\deathmatch\1\dmflags\1040\fraglimit\0\game\opentdm\gamedate\Feb 9 2013\gamedir\opentdm\gamename\OpenTDM\hostname\PlayGround.ru - Teamplay\mapname\q2dm1\match_type\TDM\maxclients\12\port\27911\protocol\34\revision\22\time_remaining\WARMUP\timelimit\0\version\q2proded r1504~924ff39 Dec 3 2014 Linux i386 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{"212.42.38.88:27911":{"Score_A":"WARMUP","Score_B":"WARMUP","anticheat":"1","cheats":"0","clients":0,"deathmatch":"1","dmflags":"1040","fraglimit":"0","game":"opentdm","gamedate":"Feb 9 2013","gamedir":"opentdm","gamename":"OpenTDM","gq_address":"212.42.38.88","gq_joinlink":"","gq_name":"Quake 2 Server","gq_online":true,"gq_port_client":27911,"gq_port_query":27911,"gq_protocol":"quake2","gq_transport":"udp","gq_type":"quake2","hostname":"PlayGround.ru - Teamplay","mapname":"q2dm1","match_type":"TDM","maxclients":"12","mod":0,"password":0,"port":"27911","protocol":"34","revision":"22","time_remaining":"WARMUP","timelimit":"0","version":"q2proded r1504~924ff39 Dec 3 2014 Linux i386"}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
<?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\Tests\Protocols; | ||
|
||
class Quake2 extends Base | ||
{ | ||
|
||
/** | ||
* Holds stub on setup | ||
* | ||
* @type \GameQ\Protocols\Quake2 | ||
*/ | ||
protected $stub; | ||
|
||
/** | ||
* Holds the expected packets for this protocol class | ||
* | ||
* @type array | ||
*/ | ||
protected $packets = [ | ||
\GameQ\Protocol::PACKET_STATUS => "\xFF\xFF\xFF\xFFstatus\x00", | ||
]; | ||
|
||
/** | ||
* Setup | ||
*/ | ||
public function setUp() | ||
{ | ||
|
||
// Create the stub class | ||
$this->stub = $this->getMock('\GameQ\Protocols\Quake2', null, [[]]); | ||
} | ||
|
||
/** | ||
* Test the packets to make sure they are correct for source | ||
*/ | ||
public function testPackets() | ||
{ | ||
|
||
// Test to make sure packets are defined properly | ||
$this->assertEquals($this->packets, \PHPUnit_Framework_Assert::readAttribute($this->stub, 'packets')); | ||
} | ||
|
||
/** | ||
* Test invalid packet type without debug | ||
*/ | ||
public function testInvalidPacketType() | ||
{ | ||
|
||
// Read in a quake 2 source file | ||
$source = file_get_contents(sprintf('%s/Providers/Quake2/1_response.txt', __DIR__)); | ||
|
||
// Change the first packet to some unknown header | ||
$source = str_replace("\xFF\xFF\xFF\xFFprint", "\xFF\xFF\xFF\xFFprints", $source); | ||
|
||
// Should show up as offline | ||
$testResult = $this->queryTest('127.0.0.1:27910', 'quake2', explode(PHP_EOL . '||' . PHP_EOL, $source), false); | ||
|
||
$this->assertFalse($testResult['gq_online']); | ||
} | ||
|
||
/** | ||
* Test for invalid packet type in response | ||
* | ||
* @expectedException Exception | ||
* @expectedExceptionMessage GameQ\Protocols\Quake2::processResponse response type | ||
* 'ffffffff7072696e7473' is not valid | ||
*/ | ||
public function testInvalidPacketTypeDebug() | ||
{ | ||
|
||
// Read in a quake 2 source file | ||
$source = file_get_contents(sprintf('%s/Providers/Quake2/1_response.txt', __DIR__)); | ||
|
||
// Change the first packet to some unknown header | ||
$source = str_replace("\xFF\xFF\xFF\xFFprint", "\xFF\xFF\xFF\xFFprints", $source); | ||
|
||
// Should show up as offline | ||
$this->queryTest('127.0.0.1:27910', 'quake2', explode(PHP_EOL . '||' . PHP_EOL, $source), true); | ||
} | ||
|
||
/** | ||
* Test responses for Quake3 | ||
* | ||
* @dataProvider loadData | ||
* | ||
* @param $responses | ||
* @param $result | ||
*/ | ||
public function testResponses($responses, $result) | ||
{ | ||
|
||
// Pull the first key off the array this is the server ip:port | ||
$server = key($result); | ||
|
||
$testResult = $this->queryTest( | ||
$server, | ||
'quake2', | ||
$responses | ||
); | ||
|
||
$this->assertEquals($result[$server], $testResult); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters