#29 Replace Quakestat

This commit is contained in:
Ulrich Block 2013-11-27 20:26:19 +01:00
parent 3d295e3def
commit 8b9cb56857
116 changed files with 12251 additions and 508 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -95,6 +95,79 @@ Unfortunately errors have slipped in 4.10. In addition the update revealed that
$query = $sql->prepare("INSERT INTO `modules` (`id`,`file`,`get`,`sub`,`type`,`active`) VALUES (5,'','le','','C','N') ON DUPLICATE KEY UPDATE `active`=VALUES(`active`)");
$query->execute();
}
$query = $sql->prepare("ALTER TABLE `servertypes` ADD COLUMN `gameq` varchar(255) NULL AFTER `qstat`");
$query->execute();
// Most accurate based on appID
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='css' WHERE `appID`=232330 LIMIT 1");
$query->execute();
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='dods' WHERE `appID`=232290 LIMIT 1");
$query->execute();
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='l4d' WHERE `appID`=550 LIMIT 1");
$query->execute();
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='l4d2' WHERE `appID`=222860 LIMIT 1");
$query->execute();
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='aoc' WHERE `appID`=17515 LIMIT 1");
$query->execute();
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='hl2dm' WHERE `appID`=232370 LIMIT 1");
$query->execute();
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='insurgency' WHERE `appID`=17705 LIMIT 1");
$query->execute();
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='tf2' WHERE `appID`=232250 LIMIT 1");
$query->execute();
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='csgo' WHERE `appID`=740 LIMIT 1");
$query->execute();
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='killingfloor' WHERE `appID`=215360 LIMIT 1");
$query->execute();
// Accurate, based on easy-wi/qstat query
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='minecraft' WHERE `qstat`='minecraft'");
$query->execute();
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='samp' WHERE `qstat`='gtasamp'");
$query->execute();
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='Mta' WHERE `qstat`='mtasa'");
$query->execute();
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='teeworlds' WHERE `qstat`='teeworlds'");
$query->execute();
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='warsow' WHERE `qstat`='warsows'");
$query->execute();
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='et' WHERE `qstat`='woets'");
$query->execute();
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='ut' WHERE `qstat`='uns'");
$query->execute();
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='ut2004' WHERE `qstat`='ut2004s'");
$query->execute();
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='ut3' WHERE `qstat`='ut2s'");
$query->execute();
// Less accurate, based on shorten
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='dod' WHERE `shorten`='dod' LIMIT 1");
$query->execute();
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='cs16' WHERE `shorten`='cstrike' LIMIT 1");
$query->execute();
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='cscz' WHERE `shorten`='czero' LIMIT 1");
$query->execute();
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='tfc' WHERE `shorten`='tfc' LIMIT 1");
$query->execute();
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='cod' WHERE `shorten`='cod' LIMIT 1");
$query->execute();
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='cod2' WHERE `shorten`='cod2' LIMIT 1");
$query->execute();
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='cod4' WHERE `shorten`='cod4' LIMIT 1");
$query->execute();
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='codmw3' WHERE `shorten`='codmw3' LIMIT 1");
$query->execute();
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='coduo' WHERE `shorten`='coduo' LIMIT 1");
$query->execute();
$query = $sql->prepare("UPDATE `servertypes` SET `gameq`='codwaw' WHERE `shorten`='codwaw' LIMIT 1");
$query->execute();
$query = $sql->prepare("DROP TABLE `qstatshorten`");
$query->execute();
} else {
echo "Error: this file needs to be included by the updater!<br />";
}

View File

@ -64,6 +64,7 @@ include(EASYWIDIR . '/stuff/settings.php');
include(EASYWIDIR . '/stuff/ssh_exec.php');
include(EASYWIDIR . '/stuff/class_voice.php');
include(EASYWIDIR . '/stuff/queries.php');
include(EASYWIDIR . '/third_party/gameq/GameQ.php');
include(EASYWIDIR . '/stuff/keyphrasefile.php');
set_time_limit($timelimit);
@ -83,23 +84,30 @@ if (!isset($ip) or $ui->escaped('SERVER_ADDR', 'server') == $ip or in_array($ip,
$vosprache = getlanguagefile('voice','uk',0);
$sprache = getlanguagefile('gserver','uk',0);
// lendmodul active ?
$query = $sql->prepare("SELECT `active` FROM `modules` WHERE `id`=5 LIMIT 1");
$query->execute();
$lendActive = $query->fetchColumn();
$lendActive = (active_check($lendActive)) ? $lendActive : 'Y';
# Pick up Reseller and Lend Settings
$resellersettings = array();
$query = $sql->prepare("SELECT `brandname`,`noservertag`,`nopassword`,`tohighslots`,`down_checks`,`resellerid` FROM `settings`");
$query2 = $sql->prepare("SELECT `active`,`shutdownempty`,`shutdownemptytime`,`lastcheck`,`oldcheck` FROM `lendsettings` WHERE `resellerid`=? LIMIT 1");
$query2 = $sql->prepare("SELECT `shutdownempty`,`shutdownemptytime`,`lastcheck`,`oldcheck` FROM `lendsettings` WHERE `resellerid`=? LIMIT 1");
$query->execute();
foreach ($query->fetchall(PDO::FETCH_ASSOC) as $row) {
unset($active);
unset($shutdownempty);
$resellerid = $row['resellerid'];
$query2->execute(array($resellerid));
foreach ($query2->fetchall(PDO::FETCH_ASSOC) as $row2) {
$active = $row2['active'];
$shutdownempty = $row2['shutdownempty'];
$shutdownemptytime = $row2['shutdownemptytime'];
$firstcheck='00-00-'.round(2*(strtotime($row2['lastcheck'])-strtotime($row2['oldcheck']))/60);
$firstchecktime=date('d-G-i');
}
if (isset($active)) $resellersettings[$resellerid] = array('active' => $active,'shutdownempty' => $shutdownempty,'shutdownemptytime' => $shutdownemptytime,'firstchecktime' => $firstchecktime,'firstcheck' => $firstcheck,'brandname' => $row['brandname'], 'noservertag' => $row['noservertag'], 'nopassword' => $row['nopassword'], 'tohighslots' => $row['tohighslots'], 'down_checks' => $row['down_checks']);
if (isset($shutdownempty)) {
$resellersettings[$resellerid] = array('shutdownempty' => $shutdownempty,'shutdownemptytime' => $shutdownemptytime,'firstchecktime' => $firstchecktime,'firstcheck' => $firstcheck,'brandname' => $row['brandname'], 'noservertag' => $row['noservertag'], 'nopassword' => $row['nopassword'], 'tohighslots' => $row['tohighslots'], 'down_checks' => $row['down_checks']);
}
}
$query = $sql->prepare("UPDATE `lendsettings` SET `oldcheck`=`lastcheck`,`lastcheck`=NOW()");
$query->execute();
@ -107,188 +115,7 @@ if (!isset($ip) or $ui->escaped('SERVER_ADDR', 'server') == $ip or in_array($ip,
# Game Server
if ($checkTypeOfServer == 'all' or $checkTypeOfServer == 'gs') {
function statushandle() {
global $userid, $resellersettings, $resellerid, $serverid, $logdate, $aeskey, $address, $gametype, $war, $status, $password, $lendserver, $elapsed, $shutdownemptytime, $numplayers, $maxplayers, $slots, $brandname, $name, $map, $secnotified, $notified, $sql, $ssprache, $lid;
list($serverip, $port) = explode(':', $address);
$returnCmd = array();
// Check lendserver specific settings
if ($lendserver == 'Y') {
// Running but no lend information in temp table
if ($status == 'UP') {
$query = $sql->prepare("SELECT 1 FROM `lendedserver` WHERE `id`=? LIMIT 1");
$query->execute(array($lid));
if ($query->rowCount() == 0) {
print "Will stop lendserver $address with lendID $lid because not lendet\r\n";
$stopserver = true;
}
if (!isset($stopserver) and $lendserver == 'Y' and $resellersettings[$resellerid]['active'] == 'Y' and $resellersettings[$resellerid]['shutdownempty'] == 'Y' and $elapsed > $shutdownemptytime and $numplayers == 0 and $maxplayers != 0 and $slots != 0) {
print "Will stop server $address after $elapsed minutes, because it is empty and threshold is $shutdownemptytime minutes \r\n";
$stopserver = true;
}
}
// Expected to be running but is not, so remove from temp table
if (isset($stopserver) or $status != 'UP') {
if (!isset($stopserver)) {
print "Will remove lendserver $address with lendID $lid because it is lendet but stopped \r\n";
}
$doNotRestart = true;
$query = $sql->prepare("DELETE FROM `lendedserver` WHERE `id`=? LIMIT 1");
$query->execute(array($lid));
}
}
if ($status == 'UP') {
$rulebreak = array();
if ($war == 'Y' and $password == 'N') {
$rulebreak[] = $ssprache->nopassword;
if ($resellersettings[$resellerid]['nopassword'] == 1) {
$stopserver = true;
print "Will stop server $address because running without password. The name converted to ISO-8859-1 is ".iconv('UTF-8','ISO-8859-1//TRANSLIT', $name).".\r\n";
} else {
print "Server with address $address is running as $gametype and illegal without password. The name converted to ISO-8859-1 is ".iconv('UTF-8','ISO-8859-1//TRANSLIT', $name).".\r\n";
}
}
if ($maxplayers > $slots) {
$rulebreak[] = $ssprache->tohighslots;
if ($resellersettings[$resellerid]['tohighslots'] == 1) {
$stopserver = true;
print "Will stop server $address because running with to much slots. The name converted to ISO-8859-1 is ".iconv('UTF-8','ISO-8859-1//TRANSLIT', $name).".\r\n";
} else {
print "Server $address is running as $gametype and with illegal slotamount. The name converted to ISO-8859-1 is ".iconv('UTF-8','ISO-8859-1//TRANSLIT', $name).".\r\n";
}
}
if ($brandname == 'Y' and $resellersettings[$resellerid]['brandname'] != '' and strpos(strtolower($name),strtolower($resellersettings[$resellerid]['brandname'])) === false) {
$rulebreak[] = $ssprache->noservertag;
if ($resellersettings[$resellerid]['noservertag'] == 1) {
$stopserver = true;
print "Will stop server $address because running without servertag. The name converted to ISO-8859-1 is ".iconv('UTF-8','ISO-8859-1//TRANSLIT', $name).".\r\n";
} else {
print "Server $address is running as $gametype and illegal without servertag. The name converted to ISO-8859-1 is ".iconv('UTF-8','ISO-8859-1//TRANSLIT', $name).".\r\n";
}
}
if (count($rulebreak) == 0 and !isset($stopserver)) {
print "Server $address is running as $gametype. The name converted to ISO-8859-1 is ".iconv('UTF-8','ISO-8859-1//TRANSLIT', $name).".\r\n";
}
if ($secnotified == 'N' and count($rulebreak) > 0) {
if ($resellerid==0) {
$query = $sql->prepare("SELECT `id`,`mail_securitybreach` FROM `userdata` WHERE `id`=? OR (`resellerid`=0 AND `accounttype`='a')");
$query->execute(array($userid));
} else {
$query = $sql->prepare("SELECT `id`,`mail_securitybreach` FROM `userdata` WHERE `id`=? OR (`id`=? AND `accounttype`='r')");
$query->execute(array($userid, $resellerid));
}
foreach ($query->fetchall(PDO::FETCH_ASSOC) as $row) {
if ($row['mail_securitybreach'] == 'Y') {
sendmail('emailsecuritybreach', $row['id'], $address, implode('<br>', $rulebreak));
}
}
$query = $sql->prepare("UPDATE `gsswitch` SET `secnotified`='Y' WHERE `serverip`=? AND `port`=? LIMIT 1");
$query->execute(array($serverip, $port));
}
if ($secnotified == 'Y' and count($rulebreak) == 0) {
$query = $sql->prepare("UPDATE `gsswitch` SET `secnotified`='N' WHERE `serverip`=? AND `port`=? LIMIT 1");
$query->execute(array($serverip, $port));
}
if (isset($stopserver)) {
$numplayers = 0;
$map = '';
$tmp = gsrestart($serverid,'so', $aeskey, $resellerid);
if (is_array($tmp)) {
foreach($tmp as $t) {
$returnCmd[] = $t;
}
}
$query = $sql->prepare("DELETE FROM `lendedserver` WHERE `serverid`=? AND `resellerid`=? AND `servertype`='g' LIMIT 1");
$query->execute(array($serverid, $resellerid));
}
if ($notified > 0) {
$query = $sql->prepare("UPDATE `gsswitch` SET `notified`=0 WHERE `serverip`=? AND `port`=? LIMIT 1");
$query->execute(array($serverip, $port));
}
} else {
$name = 'OFFLINE';
$numplayers = 0;
$maxplayers = 0;
$map = '';
$password = 'Y';
if (!isset($doNotRestart)) {
$notified++;
$query = $sql->prepare("SELECT `autoRestart` FROM `gsswitch` WHERE `serverip`=? and `port`=? LIMIT 1");
$query->execute(array($serverip, $port));
if ($query->fetchColumn() == 'Y' and $notified >= $resellersettings[$resellerid]['down_checks']) {
print "Restarting: $address\r\n";
$tmp = gsrestart($serverid, 're', $aeskey, $resellerid);
if (is_array($tmp)) {
foreach($tmp as $t) {
$returnCmd[] = $t;
}
}
} else {
print "Not Restarting: $address\r\n";
}
if ($notified == $resellersettings[$resellerid]['down_checks']) {
$query = $sql->prepare("SELECT `mail_serverdown` FROM `userdata` WHERE `id`=? LIMIT 1");
$query->execute(array($userid));
foreach ($query->fetchall(PDO::FETCH_ASSOC) as $row) {
if ($row['mail_serverdown'] == 'Y') {
sendmail('emaildownrestart', $userid, $address,'');
}
}
}
}
}
$query = $sql->prepare("UPDATE `gsswitch` SET `queryName`=?,`queryNumplayers`=?,`queryMaxplayers`=?,`queryMap`=?,`queryPassword`=?,`queryUpdatetime`=?,`notified`=? WHERE `serverip`=? and `port`=? LIMIT 1");
$query->execute(array($name, $numplayers, $maxplayers, $map, $password, $logdate, $notified, $serverip, $port));
return $returnCmd;
}
// Lend server stopping.
// We want only one socket per root server. Collect the to be stopped lendservers in an array and sort by root ID
$rtmp = array();
@ -334,303 +161,280 @@ if (!isset($ip) or $ui->escaped('SERVER_ADDR', 'server') == $ip or in_array($ip,
}
}
// Define basic variables for GS status checks
$other = array();
$i = 1;
$totalcount = 0;
$queries = array();
$totalCount = 0;
$serverBatchArray = array();
$allServersArray = array();
$shellCmds = array();
// Get the list of servers which are active and are not stopped
$query = $sql->prepare("SELECT g.`id`,g.`serverid`,g.`serverip`,g.`port`,t.`qstat` FROM `gsswitch` g INNER JOIN `serverlist` s ON g.`serverid`=s.`id` INNER JOIN `servertypes` t ON s.`servertype`=t.`id` WHERE g.`stopped`='N' AND g.`active`='Y'");
// Get the list of servers which are active and are not stopped. The array to be created will support batch mode.
$query = $sql->prepare("SELECT g.`id`,g.`rootID`,g.`serverid`,g.`serverip`,g.`port`,g.`port2`,t.`gameq` FROM `gsswitch` g INNER JOIN `serverlist` s ON g.`serverid`=s.`id` INNER JOIN `servertypes` t ON s.`servertype`=t.`id` WHERE g.`stopped`='N' AND g.`active`='Y'");
$query->execute();
foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) {
$qstat = $row['qstat'];
$serverip = $row['serverip'];
$port = $row['port'];
$server = $serverip . ':' . $port;
if (!in_array($qstat, array('', null, false))) {
// without the gameq value we cannot query. So this results need to be sorted out.
if (!in_array($row['gameq'], array('', null, false))) {
if (in_array($qstat, array('minecraft', 'tm', 'gtasamp', 'teeworlds', 'mtasa'))) {
$other[] = array('qstat' => $qstat, 'switchID' => $row['id']);
} else {
$queries[] = '-' . $qstat . ' ' . $server;
$i++;
}
$serverBatchArray[] = array('id' => $row['id'], 'type' => $row['gameq'], 'host' => $row['serverip'] . ':' . $row['port']);
$i++;
if ($i == 50) {
$querry_array[] = implode(' ', $queries);
$queries = array();
$allServersArray[] = $serverBatchArray;
$serverBatchArray = array();
$i = 1;
}
$totalcount++;
$totalCount++;
}
}
$querry_array[] = implode(' ', $queries);
$allServersArray[] = $serverBatchArray;
print "Checking $totalcount server\r\n";
$shellCmds = array();
foreach ($querry_array as $querystring) {
print "Checking $totalCount server(s) with GameQ query\r\n";
$xml = array();
foreach ($allServersArray as $servers) {
$gq = new GameQ();
$gq->setOption('timeout', 3);
unset($xmlquakestring);
print "The Quakestat Querystring is: ".$querystring . "\r\n";
ob_start();
if ($querystring != '') {
passthru(escapeshellcmd("/usr/bin/quakestat -xml -R -utf8 $querystring -sort i"));
$xmlquakestring = ob_get_contents();
}
ob_end_clean();
if (isset($xmlquakestring)) {
$xml = @simplexml_load_string($xmlquakestring);
if (isset($dbConnect['debug']) and $dbConnect['debug'] == 1) {
$gq->setOption('debug', true);
}
if (!is_array($xml) and !is_object($xml)) {
$xml = array();
}
$gq->setFilter('normalise');
$gq->addServers($servers);
unset($badstatus);
unset($badquery);
unset($badxml);
unset($badquerystring);
foreach($gq->requestData() as $switchID => $v) {
foreach ($xml as $xml2) {
$address = $xml2['address'];
list($ip, $port) = explode(':', $address);
$query = $sql->prepare("SELECT t.`qstat` FROM `gsswitch` g INNER JOIN `serverlist` s ON g.`serverid`=s.`id` INNER JOIN `servertypes` t ON s.`servertype`=t.`id` WHERE g.`serverip`=? AND g.`port`=? AND g.`active`='Y' LIMIT 1");
$query->execute(array($ip, $port));
$qstat = $query->fetchColumn();
if ($xml2['status'] == 'DOWN' or $xml2['status'] == 'TIMEOUT') {
if (isset($badquery)) {
$badquery .= ' -' . $qstat . ' ' . $address;
} else {
$badquery = ' -' . $qstat . ' ' . $address;
}
}
}
$badstatus = array();
if (isset($badquery) and $badquery != '') {
print "The recheck Querystring is: $badquery\r\n";
ob_start();
passthru(escapeshellcmd("/usr/bin/quakestat -xml -R -utf8 $badquery -sort i"));
$badquerystring=ob_get_contents();
ob_end_clean();
$badxml=simplexml_load_string($badquerystring);
foreach ($badxml as $badxml2) {
if (isset($badxml2['address']) and isip($badxml2['address'], 'ipx')) {
$address = $badxml2['address'];
$status = $badxml2['status'];
$badstatus[$address] = array('status' => $status);
if ($badxml2['status'] == 'UP') {
if ($badxml2['type'] != 'A2S') {
$gametype = $badxml2->gametype;
}
foreach ($badxml2->rules->rule as $rule) {
switch((string) $rule['name']) {
case 'gamename':
$gametype = $rule;
break;
}
}
$name = $badxml2->name;
$numplayers = $badxml2->numplayers;
$maxplayers = $badxml2->maxplayers;
$map = $badxml2->map;
$badstatus[$address] = array('gametype' => $gametype,'name' => $name,'numplayers' => $numplayers,'maxplayers' => $maxplayers,'map' => $map,'rules' => $badxml2->rules->rule);
}
}
}
}
if (!is_array($xml) and !is_object($xml)) $xml = array();
foreach ($xml as $xml2) {
unset($userid);
$lid = 0;
unset($war);
$address = $xml2['address'];
$password = '';
$addressarray = explode(':', $address);
$ip = $addressarray[0];
$port = $addressarray[1];
$query = $sql->prepare("SELECT g.*,t.`shorten`,t.`qstat` FROM `gsswitch` g INNER JOIN `serverlist` s ON g.`serverid`=s.`id` INNER JOIN `servertypes` t ON s.`servertype`=t.`id` WHERE g.`serverip`=? AND g.`port`=? LIMIT 1");
$query->execute(array($ip, $port));
$elapsed = 0;
$shutdownemptytime = 0;
$query = $sql->prepare("SELECT s.`id`,t.`description`,g.`serverip`,g.`port`,g.`port2`,g.`slots`,g.`war`,g.`brandname`,g.`secnotified`,g.`notified`,g.`lendserver`,g.`userid`,g.`resellerid`,g.`rootID` FROM `gsswitch` g INNER JOIN `serverlist` s ON g.`serverid`=s.`id` INNER JOIN `servertypes` t ON s.`servertype`=t.`id` WHERE g.`id`=? LIMIT 1");
$query2 = $sql->prepare("SELECT `id`,`started` FROM `lendedserver` WHERE `serverid`=? LIMIT 1");
$query->execute(array($switchID));
foreach ($query->fetchall(PDO::FETCH_ASSOC) as $row) {
$serverid = $row['id'];
$rootID = $row['rootID'];
$resellerid = $row['resellerid'];
$lendserver = $row['lendserver'];
$serverip = $row['serverip'];
$port = $row['port'];
$address = $row['serverip'] . ':' . $row['port'];
$gametype = $row['description'];
$notified = $row['notified'];
$secnotified = $row['secnotified'];
$userid = $row['userid'];
$qstat = $row['qstat'];
$shorten = $row['shorten'];
$brandname = $row['brandname'];
$lendserver = $row['lendserver'];
$slots = $row['slots'];
$userid = $row['userid'];
$resellerid = $row['resellerid'];
$brandname = $row['brandname'];
$rootID = $row['rootID'];
$war = $row['war'];
if ($row['tvenable'] == 'Y') {
$slots++;
}
if ($lendserver == 'Y' and $resellersettings[$resellerid]['active'] == 'Y' and $resellersettings[$resellerid]['shutdownempty'] == 'Y') {
if ($lendserver == 'Y' and $lendActive == 'Y' and $resellersettings[$resellerid]['shutdownempty'] == 'Y') {
$shutdownemptytime = $resellersettings[$resellerid]['shutdownemptytime'];
$query2 = $sql->prepare("SELECT `id`,`started` FROM `lendedserver` WHERE `serverid`=? LIMIT 1");
$query2->execute(array($serverid));
$query2->execute(array($row['id']));
foreach ($query2->fetchall(PDO::FETCH_ASSOC) as $row2) {
$lid = $row2['id'];
$elapsed = round((strtotime('now')-strtotime($row2['started']))/60);
$elapsed = round((strtotime('now') - strtotime($row2['started'])) / 60);
}
}
}
$query = $sql->prepare("SELECT `qstatpassparam` FROM `servertypes` WHERE `shorten`=? LIMIT 1");
$query->execute(array($shorten));
foreach ($query->fetchall(PDO::FETCH_ASSOC) as $row) {
$qstatpassparam = $row['qstatpassparam'];
$passparams = explode(':', $qstatpassparam);
}
unset($password, $rulebreak, $maxplayers, $name);
if ((!isset($xml2['status']) or $xml2['status'] == 'DOWN' or $xml2['status'] == 'TIMEOUT') and (!isset($badstatus[$address]['status']) or $badstatus[$address]['status'] == 'DOWN' or $badstatus[$address]['status'] == 'TIMEOUT')) {
$status='DOWN';
$name = '';
$numplayers = 0;
$maxplayers = 0;
$map = '';
$gametype = '';
print "recheck status for $address is still: $status\r\n";
} else if ((!$xml2['status'] or $xml2['status'] == 'DOWN' or $xml2['status'] == 'TIMEOUT') and isset($badstatus[$address]['status']) and $badstatus[$address]['status'] == 'UP') {
$status = 'UP';
foreach ($badstatus[$address]['rules'] as $rule) {
switch((string) $rule['name']) {
case $passparams[0]:
if ($rule == $passparams[1]) $password = 'Y';
else $password = 'N';
break;
}
}
$name = $badstatus[$address]['name'];
$numplayers = $badstatus[$address]['numplayers'];
$maxplayers = $badstatus[$address]['maxplayers'];
$map = $badstatus[$address]['map'];
$gametype = $badstatus[$address]['gametype'];
if ($v['gq_online'] == 1) {
$name = $v['gq_hostname'];
$numplayers = $v['gq_numplayers'];
$maxplayers = $v['gq_maxplayers'];
$map = $v['gq_mapname'];
$password = ($v['gq_password'] == 1) ? 'Y' : 'N';
} else {
$status = 'UP';
$name = $xml2->name;
$numplayers = $xml2->numplayers;
$maxplayers = $xml2->maxplayers;
$map = $xml2->map;
$type = $xml2['type'];
if ($type != 'A2S') {
$gametype = $xml2->gametype;
}
foreach ($xml2->rules->rule as $rule) {
switch((string) $rule['name']) {
case 'gamename':
$gametype = $rule;
break;
case $passparams[0]:
if ($rule == $passparams[1]) $password = 'Y';
else $password = 'N';
break;
}
}
}
if (!isset($password) or $password == '') {
$password = 'N';
}
if (!isset($elapsed)) {
$elapsed = 0;
}
if (!isset($shutdownemptytime)) {
$shutdownemptytime = 0;
}
if (isset($war)) {
$tmp = statushandle();
if (is_array($tmp)) {
foreach($tmp as $t) {
$shellCmds[$rootID][] = $t;
}
}
}
}
}
print "Checking Gameserver with Easy-Wi query:\r\n";
foreach ($other as $array) {
unset($userid, $serverid);
$lid = 0;
$qstat = $array['qstat'];
$serverid = $array['switchID'];
$query = $sql->prepare("SELECT s.`id`,t.`description`,g.`serverip`,g.`port`,g.`port2`,g.`slots`,g.`war`,g.`brandname`,g.`secnotified`,g.`notified`,g.`lendserver`,g.`userid`,g.`resellerid`,g.`rootID` FROM `gsswitch` g INNER JOIN `serverlist` s ON g.`serverid`=s.`id` INNER JOIN `servertypes` t ON s.`servertype`=t.`id` WHERE g.`id`=? LIMIT 1");
$query2 = $sql->prepare("SELECT `id`,`started` FROM `lendedserver` WHERE `serverid`=? LIMIT 1");
$query->execute(array($serverid));
foreach ($query->fetchall(PDO::FETCH_ASSOC) as $row) {
$serverip = $row['serverip'];
$port = $row['port'];
$address = $row['serverip'] . ':' . $row['port'];
$gametype = $row['description'];
$notified = $row['notified'];
$secnotified = $row['secnotified'];
$lendserver = $row['lendserver'];
$slots = $row['slots'];
$userid = $row['userid'];
$resellerid = $row['resellerid'];
$brandname = $row['brandname'];
$rootID = $row['rootID'];
$war = $row['war'];
if ($lendserver == 'Y' and $resellersettings[$resellerid]['active'] == 'Y' and $resellersettings[$resellerid]['shutdownempty'] == 'Y') {
$shutdownemptytime = $resellersettings[$resellerid]['shutdownemptytime'];
$query2->execute(array($row['id']));
foreach ($query2->fetchall(PDO::FETCH_ASSOC) as $row2) {
$lid = $row2['id'];
$elapsed = round((strtotime('now') - strtotime($row2['started'])) / 60);
}
}
}
if (isset($userid) and isset($serverid) and isset($port) and isset($qstat) and isset($serverip)) {
$status = 'UP';
if (in_array($qstat, array('gtasamp', 'minecraft', 'teeworlds', 'mtasa'))) {
echo "$qstat\r\n";
$query = ($qstat == 'mtasa') ? serverQuery($serverip, ($port + 123), $qstat) : serverQuery($serverip, $port, $qstat);
if (is_array($query)) {
$name = $query['hostname'];
$password = ($query['password'] == 1) ? 'Y' : 'N';
$numplayers = $query['players'];
$maxplayers = $query['slots'];
$map = $query['map'];
} else {
$status = 'DOWN';
$name = $gametype . 'OFFLINE';
$numplayers = 0;
$maxplayers = 0;
$map = '';
$password = 'Y';
}
} else {
$brandname = 'N';
$name = $gametype . 'ONLINE';
$name = 'OFFLINE';
$numplayers = 0;
$maxplayers = 0;
$map = '';
$password = 'Y';
}
if (!isset($elapsed)) {
$elapsed = 0;
}
if (!isset($shutdownemptytime)) {
$shutdownemptytime = 0;
}
$tmp = statushandle();
if (is_array($tmp)) {
foreach($tmp as $t) {
$shellCmds[$rootID][] = $t;
$returnCmd = array();
// Check lendserver specific settings
if (isset($userid) and $lendserver == 'Y') {
// Running but no lend information in temp table
if ($v['gq_online'] == 1) {
$query = $sql->prepare("SELECT 1 FROM `lendedserver` WHERE `id`=? LIMIT 1");
$query->execute(array($lid));
if ($query->rowCount() == 0) {
print "Will stop lendserver $address with lendID $lid because not lendet\r\n";
$stopserver = true;
}
if (!isset($stopserver) and $lendserver == 'Y' and $lendActive == 'Y' and $resellersettings[$resellerid]['shutdownempty'] == 'Y' and $elapsed > $shutdownemptytime and $numplayers == 0 and $maxplayers != 0 and $slots != 0) {
print "Will stop server $address after $elapsed minutes, because it is empty and threshold is $shutdownemptytime minutes \r\n";
$stopserver = true;
}
}
// Expected to be running but is not, so remove from temp table
if (isset($stopserver) or $v['gq_online'] != 1) {
if (!isset($stopserver)) {
print "Will remove lendserver $address with lendID $lid because it is lendet but stopped \r\n";
}
$doNotRestart = true;
$query = $sql->prepare("DELETE FROM `lendedserver` WHERE `id`=? LIMIT 1");
$query->execute(array($lid));
}
}
if (isset($userid) and $v['gq_online'] == 1) {
$rulebreak = array();
if ($war == 'Y' and $password == 'N') {
$rulebreak[] = $ssprache->nopassword;
if ($resellersettings[$resellerid]['nopassword'] == 1) {
$stopserver = true;
print "Will stop server $address because running without password. The name converted to ISO-8859-1 is ".iconv('UTF-8','ISO-8859-1//TRANSLIT', $name).".\r\n";
} else {
print "Server with address $address is running as $gametype and illegal without password. The name converted to ISO-8859-1 is ".iconv('UTF-8','ISO-8859-1//TRANSLIT', $name).".\r\n";
}
}
if ($maxplayers > $slots) {
$rulebreak[] = $ssprache->tohighslots;
if ($resellersettings[$resellerid]['tohighslots'] == 1) {
$stopserver = true;
print "Will stop server $address because running with to much slots. The name converted to ISO-8859-1 is ".iconv('UTF-8','ISO-8859-1//TRANSLIT', $name).".\r\n";
} else {
print "Server $address is running as $gametype and with illegal slotamount. The name converted to ISO-8859-1 is ".iconv('UTF-8','ISO-8859-1//TRANSLIT', $name).".\r\n";
}
}
if ($brandname == 'Y' and $resellersettings[$resellerid]['brandname'] != '' and strpos(strtolower($name),strtolower($resellersettings[$resellerid]['brandname'])) === false) {
$rulebreak[] = $ssprache->noservertag;
if ($resellersettings[$resellerid]['noservertag'] == 1) {
$stopserver = true;
print "Will stop server $address because running without servertag. The name converted to ISO-8859-1 is ".iconv('UTF-8','ISO-8859-1//TRANSLIT', $name).".\r\n";
} else {
print "Server $address is running as $gametype and illegal without servertag. The name converted to ISO-8859-1 is ".iconv('UTF-8','ISO-8859-1//TRANSLIT', $name).".\r\n";
}
}
if (count($rulebreak) == 0 and !isset($stopserver)) {
print "Server $address is running as $gametype. The name converted to ISO-8859-1 is ".iconv('UTF-8','ISO-8859-1//TRANSLIT', $name).".\r\n";
}
if ($secnotified == 'N' and count($rulebreak) > 0) {
if ($resellerid==0) {
$query = $sql->prepare("SELECT `id`,`mail_securitybreach` FROM `userdata` WHERE `id`=? OR (`resellerid`=0 AND `accounttype`='a')");
$query->execute(array($userid));
} else {
$query = $sql->prepare("SELECT `id`,`mail_securitybreach` FROM `userdata` WHERE `id`=? OR (`id`=? AND `accounttype`='r')");
$query->execute(array($userid, $resellerid));
}
foreach ($query->fetchall(PDO::FETCH_ASSOC) as $row) {
if ($row['mail_securitybreach'] == 'Y') {
sendmail('emailsecuritybreach', $row['id'], $address, implode('<br>', $rulebreak));
}
}
$query = $sql->prepare("UPDATE `gsswitch` SET `secnotified`='Y' WHERE `id`=? LIMIT 1");
$query->execute(array($switchID));
}
if ($secnotified == 'Y' and count($rulebreak) == 0) {
$query = $sql->prepare("UPDATE `gsswitch` SET `secnotified`='N' WHERE `id`=? LIMIT 1");
$query->execute(array($switchID));
}
if (isset($stopserver)) {
$numplayers = 0;
$map = '';
$tmp = gsrestart($switchID, 'so', $aeskey, $resellerid);
if (is_array($tmp)) {
foreach($tmp as $t) {
$returnCmd[] = $t;
}
}
$query = $sql->prepare("DELETE FROM `lendedserver` WHERE `serverid`=? AND `resellerid`=? AND `servertype`='g' LIMIT 1");
$query->execute(array($switchID, $resellerid));
}
if ($notified > 0) {
$query = $sql->prepare("UPDATE `gsswitch` SET `notified`=0 WHERE `id`=? LIMIT 1");
$query->execute(array($switchID));
}
} else if (isset($userid)) {
$name = 'OFFLINE';
$numplayers = 0;
$maxplayers = 0;
$map = '';
$password = 'Y';
if (!isset($doNotRestart)) {
$notified++;
$query = $sql->prepare("SELECT `autoRestart` FROM `gsswitch` WHERE `id`=? LIMIT 1");
$query->execute(array($switchID));
if ($query->fetchColumn() == 'Y' and $notified >= $resellersettings[$resellerid]['down_checks']) {
print "Restarting: $address\r\n";
$tmp = gsrestart($switchID, 're', $aeskey, $resellerid);
if (is_array($tmp)) {
foreach($tmp as $t) {
$returnCmd[] = $t;
}
}
} else {
print "Not Restarting: $address\r\n";
}
if ($notified == $resellersettings[$resellerid]['down_checks']) {
$query = $sql->prepare("SELECT `mail_serverdown` FROM `userdata` WHERE `id`=? LIMIT 1");
$query->execute(array($userid));
foreach ($query->fetchall(PDO::FETCH_ASSOC) as $row) {
if ($row['mail_serverdown'] == 'Y') {
sendmail('emaildownrestart', $userid, $address,'');
}
}
}
}
}
$query = $sql->prepare("UPDATE `gsswitch` SET `queryName`=?,`queryNumplayers`=?,`queryMaxplayers`=?,`queryMap`=?,`queryPassword`=?,`queryUpdatetime`=?,`notified`=? WHERE `id`=? LIMIT 1");
$query->execute(array($name, $numplayers, $maxplayers, $map, $password, $logdate, $notified, $switchID));
foreach($returnCmd as $t) {
$shellCmds[$rootID][] = $t;
}
}
}
unset($gq);
foreach($shellCmds as $k => $v) {
ssh2_execute('gs', $k, $v);
}
@ -679,8 +483,8 @@ if (!isset($ip) or $ui->escaped('SERVER_ADDR', 'server') == $ip or in_array($ip,
# https://github.com/easy-wi/developer/issues/70
$sshkey = removePub($row['keyname']);
$pubkey='keys/'.$sshkey.'.pub';
$key='keys/'.$sshkey;
$pubkey = 'keys/' . $sshkey . '.pub';
$key = 'keys/' . $sshkey;
if (file_exists($pubkey) and file_exists($key)) {
$ssh2_2 = @ssh2_connect($row['ssh2ip'], $row['decryptedssh2port'], array('hostkey' => 'ssh-rsa'));
@ -710,8 +514,10 @@ if (!isset($ip) or $ui->escaped('SERVER_ADDR', 'server') == $ip or in_array($ip,
$folders = $folders . ' && ';
}
$ssh2cmd = $folders.'function r () { if [ "`ps fx | grep '.$tsdnsbin.' | grep -v grep`" == "" ]; then ./'.$tsdnsbin.' > /dev/null & else ./'.$tsdnsbin.' --update > /dev/null & fi }; r& ';
echo $ssh2cmd . "\r\n";
echo ssh2_exec($ssh2_2, $ssh2cmd) . "\r\n";
if (isset($dbConnect['debug']) and $dbConnect['debug'] == 1) {
echo $ssh2cmd . "\r\n";
}
ssh2_exec($ssh2_2, $ssh2cmd);
$ssh2_2 = null;
} else {
print "Error: Bad logindata for external tsdns ".$row['ssh2ip'] . "\r\n";
@ -856,12 +662,16 @@ if (!isset($ip) or $ui->escaped('SERVER_ADDR', 'server') == $ip or in_array($ip,
$ssh2cmd2 = $tsdnsFolders.'function r () { if [ "`ps fx | grep '.$tsdnsbin.' | grep -v grep`" == "" ]; then ./'.$tsdnsbin.' > /dev/null & else ./'.$tsdnsbin.' --update > /dev/null & fi }; r& ';
}
if ($tsdown == true) {
echo $ssh2cmd . "\r\n";
echo ssh2_exec($ssh2, $ssh2cmd);
if (isset($dbConnect['debug']) and $dbConnect['debug'] == 1) {
echo $ssh2cmd . "\r\n";
}
ssh2_exec($ssh2, $ssh2cmd);
}
if ($tsdnsdown == true) {
echo $ssh2cmd2 . "\r\n";
echo ssh2_exec($ssh2, $ssh2cmd2);
if (isset($dbConnect['debug']) and $dbConnect['debug'] == 1) {
echo $ssh2cmd2 . "\r\n";
}
ssh2_exec($ssh2, $ssh2cmd2);
}
print 'Restarting: '.$restartreturn . "\r\n";
} else {

View File

@ -40,6 +40,7 @@ if ((!isset($admin_id) or $main != 1) or (isset($admin_id) and !$pa['gimages']))
header('Location: admin.php');
die('No acces');
}
include(EASYWIDIR . '/third_party/gameq/GameQ.php');
include(EASYWIDIR . '/stuff/keyphrasefile.php');
$sprache = getlanguagefile('images', $user_language, $reseller_id);
@ -111,10 +112,9 @@ if ($ui->w('action', 4, 'post') and !token(true)) {
$configs = $ui->startparameter('configs', 'post');
$configedit = $ui->startparameter('configedit', 'post');
$modcmds = $ui->startparameter('modcmds', 'post');
$qstat = $ui->w('qstat', 20, 'post');
$gameq = $ui->w('gameq', 255, 'post');
$gamemod = $ui->active('gamemod', 'post');
$gamemod2 = $ui->w('gamemod2', 10, 'post');
$qstatpassparam = $ui->startparameter('qstatpassparam', 'post');
$portMax = ($ui->id('portMax', 1, 'post')) ? $ui->id('portMax', 1, 'post') : 1;
$portStep = ($ui->id('portStep',4, 'post')) ? $ui->id('portStep',4, 'post') : 100;
$portOne = ($ui->id('portOne',5, 'post')) ? $ui->id('portOne',5, 'post') : 27015;
@ -132,17 +132,59 @@ if ($ui->w('action', 4, 'post') and !token(true)) {
if (!$ui->smallletters('action', 2, 'post') or $ui->id('import', 1, 'post') == 1) {
// Protocol list code taken from https://github.com/Austinb/GameQ/blob/v2/examples/list.php
$protocols_path = GAMEQ_BASE."gameq/protocols/";
// Grab the dir with all the classes available
$dir = dir($protocols_path);
$protocols = array();
// Now lets loop the directories
while (false !== ($entry = $dir->read()))
{
if(!is_file($protocols_path.$entry))
{
continue;
}
// Figure out the class name
$class_name = 'GameQ_Protocols_'.ucfirst(pathinfo($entry, PATHINFO_FILENAME));
// Lets get some info on the class
$reflection = new ReflectionClass($class_name);
// Check to make sure we can actually load the class
if(!$reflection->IsInstantiable())
{
continue;
}
// Load up the class so we can get info
$class = new $class_name;
// Add it to the list
$protocols[$class->name()] = $class->name_long();
// Unset the class
unset($class);
}
// Close the directory
unset($dir);
ksort($protocols);
// GameQ protocol listing done. Easy-WI Code again.
if ($ui->st('d', 'get') == 'ad') {
$token=token();
$token = token();
$table = array();
// Collect the shorten we need for game modification
$query = $sql->prepare("SELECT DISTINCT(`shorten`) FROM `servertypes` WHERE `resellerid`=?");
$query->execute(array($reseller_id));
$table = array();
foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) $table[] = array('shorten' => $row['shorten']);
$query = $sql->prepare("SELECT `qstat`,`description` FROM `qstatshorten` ORDER BY `description`");
$query->execute();
$table2 = array();
foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) {
$table2[] = array('qstat' => $row['qstat'], 'description' => $row['description']);
$table[] = array('shorten' => $row['shorten']);
}
if ($ui->id('import', 1, 'post') == 1 and $_FILES['file']['error']==0 and $_FILES['file']['type'] == 'text/xml') {
@ -192,8 +234,8 @@ if ($ui->w('action', 4, 'post') and !token(true)) {
if ($node->nodeName == 'tic') {
$tic = $node->nodeValue;
}
if ($node->nodeName == 'qstat') {
$qstat = $node->nodeValue;
if ($node->nodeName == 'gameq') {
$gameq = $node->nodeValue;
}
if ($node->nodeName == 'gamemod') {
$gamemod = $node->nodeValue;
@ -207,9 +249,6 @@ if ($ui->w('action', 4, 'post') and !token(true)) {
if ($node->nodeName == 'configedit') {
$configedit = $node->nodeValue;
}
if ($node->nodeName == 'qstatpassparam') {
$qstatpassparam = $node->nodeValue;
}
if ($node->nodeName == 'portStep') {
$portStep = $node->nodeValue;
}
@ -262,13 +301,6 @@ if ($ui->w('action', 4, 'post') and !token(true)) {
}
}
$query = $sql->prepare("SELECT `qstat`,`description` FROM `qstatshorten`");
$query->execute();
$table3 = array();
foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) {
$selected = (isset($qstat) and $qstat == $row['qstat']) ? ' selected="selected"' : '';
$table3[] = array('option' => '<option value="' . $row['qstat'] . '" ' . $selected . '>' . $row['description'] . '</option>');
}
$template_file = 'admin_images_add.tpl';
} else if ($ui->st('d', 'get') == 'md' and $id) {
@ -288,12 +320,11 @@ if ($ui->w('action', 4, 'post') and !token(true)) {
$cmd = $row['cmd'];
$modcmds = $row['modcmds'];
$tic = $row['tic'];
$qstat = $row['qstat'];
$gameq = $row['gameq'];
$gamemod = $row['gamemod'];
$gamemod2 = $row['gamemod2'];
$configs = $row['configs'];
$configedit = $row['configedit'];
$qstatpassparam = $row['qstatpassparam'];
$appID = $row['appID'];
$portMax = $row['portMax'];
$portStep = $row['portStep'];
@ -318,13 +349,6 @@ if ($ui->w('action', 4, 'post') and !token(true)) {
$table[] = array('shorten' => $row['shorten']);
}
$query = $sql->prepare("SELECT `qstat`,`description` FROM `qstatshorten`");
$query->execute();
$table3 = array();
foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) {
$selected = (isset($qstat) and $qstat == $row['qstat']) ? ' selected="selected"' : '';
$table3[] = array('option' => '<option value="' . $row['qstat'] . '" ' . $selected . '>' . $row['description'] . '</option>');
}
$template_file = 'admin_images_md.tpl';
} else {
$template_file = 'admin_404.tpl';
@ -378,15 +402,15 @@ if ($ui->w('action', 4, 'post') and !token(true)) {
if ($ui->st('action', 'post') == 'ad' and $reseller_id == 0) {
$query = $sql->prepare("INSERT INTO `servertypes` (`iptables`,`protectedSaveCFGs`,`steamgame`,`updates`,`shorten`,`description`,`type`,`gamebinary`,`binarydir`,`modfolder`,`map`,`mapGroup`,`cmd`,`modcmds`,`qstat`,`gamemod`,`gamemod2`,`configs`,`configedit`,`qstatpassparam`,`appID`,`portMax`,`portStep`,`portOne`,`portTwo`,`portThree`,`portFour`,`portFive`,`protected`,`ramLimited`,`ftpAccess`,`os`,`resellerid`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
$query->execute(array($iptables, $protectedSaveCFGs, $steamgame, $updates, $shorten, $description, 'gserver', $gamebinary, $binarydir, $modfolder, $map, $mapGroup, $cmd, $modcmds, $qstat, $gamemod, $gamemod2, $configs, $configedit, $qstatpassparam, $appID, $portMax, $portStep, $portOne, $portTwo, $portThree, $portFour, $portFive, $protected, $ramLimited, $ftpAccess, $os,$reseller_id));
$query = $sql->prepare("INSERT INTO `servertypes` (`iptables`,`protectedSaveCFGs`,`steamgame`,`updates`,`shorten`,`description`,`type`,`gamebinary`,`binarydir`,`modfolder`,`map`,`mapGroup`,`cmd`,`modcmds`,`gameq`,`gamemod`,`gamemod2`,`configs`,`configedit`,`appID`,`portMax`,`portStep`,`portOne`,`portTwo`,`portThree`,`portFour`,`portFive`,`protected`,`ramLimited`,`ftpAccess`,`os`,`resellerid`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
$query->execute(array($iptables, $protectedSaveCFGs, $steamgame, $updates, $shorten, $description, 'gserver', $gamebinary, $binarydir, $modfolder, $map, $mapGroup, $cmd, $modcmds, $gameq, $gamemod, $gamemod2, $configs, $configedit, $appID, $portMax, $portStep, $portOne, $portTwo, $portThree, $portFour, $portFive, $protected, $ramLimited, $ftpAccess, $os,$reseller_id));
$rowCount = $query->rowCount();
$loguseraction = '%add% %template% ' . $shorten;
} else if ($ui->st('action', 'post') == 'md') {
$query = $sql->prepare("UPDATE `servertypes` SET `iptables`=?,`protectedSaveCFGs`=?,`steamgame`=?,`updates`=?,`shorten`=?,`description`=?,`gamebinary`=?,`binarydir`=?,`modfolder`=?,`map`=?,`mapGroup`=?,`cmd`=?,`modcmds`=?,`qstat`=?,`gamemod`=?,`gamemod2`=?,`configs`=?,`configedit`=?,`qstatpassparam`=?,`appID`=?,`portMax`=?,`portStep`=?,`portOne`=?,`portTwo`=?,`portThree`=?,`portFour`=?,`portFive`=?,`protected`=?,`ramLimited`=?,`ftpAccess`=?,`os`=? WHERE `id`=? AND `resellerid`=? LIMIT 1");
$query->execute(array($iptables, $protectedSaveCFGs, $steamgame, $updates, $shorten, $description, $gamebinary, $binarydir, $modfolder, $map, $mapGroup, $cmd, $modcmds, $qstat, $gamemod, $gamemod2, $configs, $configedit, $qstatpassparam, $appID, $portMax, $portStep, $portOne, $portTwo, $portThree, $portFour, $portFive, $protected, $ramLimited, $ftpAccess, $os, $ui->id('id', 10, 'get'), $reseller_id));
$query = $sql->prepare("UPDATE `servertypes` SET `iptables`=?,`protectedSaveCFGs`=?,`steamgame`=?,`updates`=?,`shorten`=?,`description`=?,`gamebinary`=?,`binarydir`=?,`modfolder`=?,`map`=?,`mapGroup`=?,`cmd`=?,`modcmds`=?,`gameq`=?,`gamemod`=?,`gamemod2`=?,`configs`=?,`configedit`=?,`appID`=?,`portMax`=?,`portStep`=?,`portOne`=?,`portTwo`=?,`portThree`=?,`portFour`=?,`portFive`=?,`protected`=?,`ramLimited`=?,`ftpAccess`=?,`os`=? WHERE `id`=? AND `resellerid`=? LIMIT 1");
$query->execute(array($iptables, $protectedSaveCFGs, $steamgame, $updates, $shorten, $description, $gamebinary, $binarydir, $modfolder, $map, $mapGroup, $cmd, $modcmds, $gameq, $gamemod, $gamemod2, $configs, $configedit, $appID, $portMax, $portStep, $portOne, $portTwo, $portThree, $portFour, $portFive, $protected, $ramLimited, $ftpAccess, $os, $ui->id('id', 10, 'get'), $reseller_id));
$rowCount = $query->rowCount();
$loguseraction = '%mod% %template% ' . $shorten;
}

View File

@ -627,15 +627,6 @@ $query = "CREATE TABLE IF NOT EXISTS `page_terms_used` (
$add = $sql->prepare($query);
$add->execute();
$query = "CREATE TABLE IF NOT EXISTS `qstatshorten` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`qstat` varchar(15) NOT NULL,
`description` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB";
$add = $sql->prepare($query);
$add->execute();
$query = "CREATE TABLE IF NOT EXISTS `resellerdata` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`useractive` enum('Y','N') NOT NULL DEFAULT 'Y',
@ -834,6 +825,7 @@ $query = "CREATE TABLE IF NOT EXISTS `servertypes` (
`cmd` text NOT NULL,
`modcmds` text,
`tic` varchar(5) DEFAULT NULL,
`gameq` varchar(255) DEFAULT NULL,
`qstat` varchar(30) DEFAULT NULL,
`gamemod` enum('Y','N') NOT NULL DEFAULT 'N',
`gamemod2` varchar(15) DEFAULT NULL,

View File

@ -549,11 +549,6 @@ $defined['page_terms_used'] = array('page_id' => array("Type"=>"int(10) unsigned
'resellerid' => array("Type"=>"int(10) unsigned","Null"=>"YES","Key"=>"MUL","Default"=>"0","Extra"=>"")
);
$defined['qstatshorten'] = array('id' => array("Type"=>"int(10) unsigned","Null"=>"NO","Key"=>"PRI","Default"=>"","Extra"=>"auto_increment"),
'qstat' => array("Type"=>"varchar(15)","Null"=>"NO","Key"=>"","Default"=>"","Extra"=>""),
'description' => array("Type"=>"varchar(50)","Null"=>"NO","Key"=>"","Default"=>"","Extra"=>"")
);
$defined['resellerdata'] = array('id' => array("Type"=>"int(10) unsigned","Null"=>"NO","Key"=>"PRI","Default"=>"","Extra"=>"auto_increment"),
'useractive' => array("Type"=>"enum('Y','N')","Null"=>"NO","Key"=>"","Default"=>"Y","Extra"=>""),
'ips' => array("Type"=>"text","Null"=>"YES","Key"=>"","Default"=>"","Extra"=>""),
@ -659,6 +654,7 @@ $defined['servertypes'] = array('id' => array("Type"=>"int(10) unsigned","Null"=
'cmd' => array("Type"=>"text","Null"=>"NO","Key"=>"","Default"=>"","Extra"=>""),
'modcmds' => array("Type"=>"text","Null"=>"YES","Key"=>"","Default"=>"","Extra"=>""),
'tic' => array("Type"=>"varchar(5)","Null"=>"YES","Key"=>"","Default"=>"","Extra"=>""),
'gameq' => array("Type"=>"varchar(255)","Null"=>"YES","Key"=>"","Default"=>"","Extra"=>""),
'qstat' => array("Type"=>"varchar(30)","Null"=>"YES","Key"=>"","Default"=>"","Extra"=>""),
'gamemod' => array("Type"=>"enum('Y','N')","Null"=>"NO","Key"=>"","Default"=>"N","Extra"=>""),
'gamemod2' => array("Type"=>"varchar(15)","Null"=>"YES","Key"=>"","Default"=>"","Extra"=>""),

View File

@ -126,10 +126,13 @@
<div class="controls"><input class="span12" id="inputShorten" type="text" name="shorten" value="<?php echo $shorten;?>"></div>
</div>
<div class="control-group">
<label class="control-label" for="inputQstat"><?php echo $sprache->qstat;?></label>
<label class="control-label" for="inputGameQ">GameQ</label>
<div class="controls">
<select class="span12" id="inputQstat" name="qstat">
<?php foreach ($table3 as $table_row3) { echo $table_row3['option'];} ?>
<select class="span12" id="inputGameQ" name="gameq">
<option value="">Other</option>
<?php foreach ($protocols as $k=>$v){ ?>
<option value="<?php echo $k;?>" <?php if($k==$gameq) echo 'selected="selected"';?>><?php echo $v;?></option>
<?php } ?>
</select>
</div>
</div>
@ -157,10 +160,6 @@
<label class="control-label" for="inputMapgroup"><?php echo $sprache->startmapgroup;?></label>
<div class="controls"><input class="span12" id="inputMapgroup" type="text" name="mapGroup" value="<?php echo $mapGroup;?>"></div>
</div>
<div class="control-group">
<label class="control-label" for="inputQstatParam"><?php echo $sprache->qstatpassparam;?></label>
<div class="controls"><input class="span12" id="inputQstatParam" type="text" name="qstatpassparam" value="<?php echo $qstatpassparam;?>"></div>
</div>
<div class="control-group">
<label class="control-label" for="inputPortMax"><?php echo $sprache->portMax;?></label>
<div class="controls">

View File

@ -106,10 +106,13 @@
<div class="controls"><input class="span12" id="inputShorten" type="text" name="shorten" value="<?php echo $shorten;?>"></div>
</div>
<div class="control-group">
<label class="control-label" for="inputQstat"><?php echo $sprache->qstat;?></label>
<label class="control-label" for="inputGameQ">GameQ</label>
<div class="controls">
<select class="span12" id="inputQstat" name="qstat">
<?php foreach ($table3 as $table_row3) { echo $table_row3['option'];} ?>
<select class="span12" id="inputGameQ" name="gameq">
<option value="">Other</option>
<?php foreach ($protocols as $k=>$v){ ?>
<option value="<?php echo $k;?>" <?php if($k==$gameq) echo 'selected="selected"';?>><?php echo $v;?></option>
<?php } ?>
</select>
</div>
</div>
@ -137,10 +140,6 @@
<label class="control-label" for="inputMapgroup"><?php echo $sprache->startmapgroup;?></label>
<div class="controls"><input class="span12" id="inputMapgroup" type="text" name="mapGroup" value="<?php echo $mapGroup;?>"></div>
</div>
<div class="control-group">
<label class="control-label" for="inputQstatParam"><?php echo $sprache->qstatpassparam;?></label>
<div class="controls"><input class="span12" id="inputQstatParam" type="text" name="qstatpassparam" value="<?php echo $qstatpassparam;?>"></div>
</div>
<div class="control-group">
<label class="control-label" for="inputPortMax"><?php echo $sprache->portMax;?></label>
<div class="controls">

871
web/third_party/gameq/GameQ.php vendored Normal file
View File

@ -0,0 +1,871 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Init some stuff
*/
// Figure out where we are so we can set the proper references
define('GAMEQ_BASE', realpath(dirname(__FILE__)).DIRECTORY_SEPARATOR);
// Define the autoload so we can require files easy
spl_autoload_register(array('GameQ', 'auto_load'));
/**
* Base GameQ Class
*
* This class should be the only one that is included when you use GameQ to query
* any games servers. All necessary sub-classes are loaded as needed.
*
* Requirements: See wiki or README for more information on the requirements
* - PHP 5.2+ (Recommended 5.3+)
* * Bzip2 - http://www.php.net/manual/en/book.bzip2.php
* * Zlib - http://www.php.net/manual/en/book.zlib.php
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ
{
/*
* Constants
*/
const VERSION = '2.0.1';
/*
* Server array keys
*/
const SERVER_TYPE = 'type';
const SERVER_HOST = 'host';
const SERVER_ID = 'id';
const SERVER_OPTIONS = 'options';
/* Static Section */
protected static $instance = NULL;
/**
* Create a new instance of this class
*/
public static function factory()
{
// Create a new instance
self::$instance = new self();
// Return this new instance
return self::$instance;
}
/**
* Attempt to auto-load a class based on the name
*
* @param string $class
* @throws GameQException
*/
public static function auto_load($class)
{
try
{
// Transform the class name into a path
$file = str_replace('_', '/', strtolower($class));
// Find the file and return the full path, if it exists
if ($path = self::find_file($file))
{
// Load the class file
require $path;
// Class has been found
return TRUE;
}
// Class is not in the filesystem
return FALSE;
}
catch (Exception $e)
{
throw new GameQException($e->getMessage(), $e->getCode(), $e);
die;
}
}
/**
* Try to find the file based on the class passed.
*
* @param string $file
*/
public static function find_file($file)
{
$found = FALSE; // By default we did not find anything
// Create a partial path of the filename
$path = GAMEQ_BASE.$file.'.php';
// Is a file so we can include it
if(is_file($path))
{
$found = $path;
}
return $found;
}
/* Dynamic Section */
/**
* Defined options by default
*
* @var array()
*/
protected $options = array(
'debug' => FALSE,
'timeout' => 3, // Seconds
'filters' => array(),
// Advanced settings
'stream_timeout' => 200000, // See http://www.php.net/manual/en/function.stream-select.php for more info
'write_wait' => 500, // How long (in micro-seconds) to pause between writting to server sockets, helps cpu usage
);
/**
* Array of servers being queried
*
* @var array
*/
protected $servers = array();
/**
* Holds the list of active sockets. This array is automaically cleaned as needed
*
* @var array
*/
protected $sockets = array();
/**
* Make new class and check for requirements
*
* @throws GameQException
* @return boolean
*/
public function __construct()
{
// @todo: Add PHP version check?
}
/**
* Get an option's value
*
* @param string $option
*/
public function __get($option)
{
return isset($this->options[$option]) ? $this->options[$option] : NULL;
}
/**
* Set an option's value
*
* @param string $option
* @param mixed $value
* @return boolean
*/
public function __set($option, $value)
{
$this->options[$option] = $value;
return TRUE;
}
/**
* Chainable call to __set, uses set as the actual setter
*
* @param string $var
* @param mixed $value
* @return GameQ
*/
public function setOption($var, $value)
{
// Use magic
$this->{$var} = $value;
return $this; // Make chainable
}
/**
* Set an output filter.
*
* @param string $name
* @param array $params
* @return GameQ
*/
public function setFilter($name, $params = array())
{
// Create the proper filter class name
$filter_class = 'GameQ_Filters_'.$name;
try
{
// Pass any parameters and make the class
$this->options['filters'][$name] = new $filter_class($params);
}
catch (GameQ_FiltersException $e)
{
// We catch the exception here, thus the filter is not applied
// but we issue a warning
error_log($e->getMessage(), E_USER_WARNING);
}
return $this; // Make chainable
}
/**
* Remove a global output filter.
*
* @param string $name
* @return GameQ
*/
public function removeFilter($name)
{
unset($this->options['filters'][$name]);
return $this; // Make chainable
}
/**
* Add a server to be queried
*
* Example:
* $this->addServer(array(
* // Required keys
* 'type' => 'cs',
* 'host' => '127.0.0.1:27015', '127.0.0.1' or 'somehost.com:27015'
* Port not required, but will use the default port in the class which may not be correct for the
* specific server being queried.
*
* // Optional keys
* 'id' => 'someServerId', // By default will use pased host info (i.e. 127.0.0.1:27015)
* 'options' => array('timeout' => 5), // By default will use global options
* ));
*
* @param array $server_info
* @throws GameQException
* @return boolean|GameQ
*/
public function addServer(Array $server_info=NULL)
{
// Check for server type
if(!key_exists(self::SERVER_TYPE, $server_info) || empty($server_info[self::SERVER_TYPE]))
{
throw new GameQException("Missing server info key '".self::SERVER_TYPE."'");
return FALSE;
}
// Check for server host
if(!key_exists(self::SERVER_HOST, $server_info) || empty($server_info[self::SERVER_HOST]))
{
throw new GameQException("Missing server info key '".self::SERVER_HOST."'");
return FALSE;
}
// Check for server id
if(!key_exists(self::SERVER_ID, $server_info) || empty($server_info[self::SERVER_ID]))
{
// Make an id so each server has an id when returned
$server_info[self::SERVER_ID] = $server_info[self::SERVER_HOST];
}
// Check for options
if(!key_exists(self::SERVER_OPTIONS, $server_info)
|| !is_array($server_info[self::SERVER_OPTIONS])
|| empty($server_info[self::SERVER_OPTIONS]))
{
// Make an id so each server has an id when returned
$server_info[self::SERVER_OPTIONS] = array();
}
// Define these
$server_id = $server_info[self::SERVER_ID];
$server_ip = '127.0.0.1';
$server_port = FALSE;
// We have an IPv6 address (and maybe a port)
if(substr_count($server_info[self::SERVER_HOST], ':') > 1)
{
// See if we have a port, input should be in the format [::1]:27015 or similar
if(strstr($server_info[self::SERVER_HOST], ']:'))
{
// Explode to get port
$server_addr = explode(':', $server_info[self::SERVER_HOST]);
// Port is the last item in the array, remove it and save
$server_port = array_pop($server_addr);
// The rest is the address, recombine
$server_ip = implode(':', $server_addr);
unset($server_addr);
}
// Just the IPv6 address, no port defined
else
{
$server_ip = $server_info[self::SERVER_HOST];
}
// Now let's validate the IPv6 value sent, remove the square brackets ([]) first
if(!filter_var(trim($server_ip, '[]'), FILTER_VALIDATE_IP, array(
'flags' => FILTER_FLAG_IPV6,
)))
{
throw new GameQException("The IPv6 address '{$server_ip}' is invalid.");
return FALSE;
}
}
// IPv4
else
{
// We have a port defined
if(strstr($server_info[self::SERVER_HOST], ':'))
{
list($server_ip, $server_port) = explode(':', $server_info[self::SERVER_HOST]);
}
// No port, just IPv4
else
{
$server_ip = $server_info[self::SERVER_HOST];
}
// Validate the IPv4 value, if FALSE is not a valid IP, maybe a hostname. Try to resolve
if(!filter_var($server_ip, FILTER_VALIDATE_IP, array(
'flags' => FILTER_FLAG_IPV4,
)))
{
// When gethostbyname() fails it returns the original string
// so if ip and the result from gethostbyname() are equal this failed.
if($server_ip === gethostbyname($server_ip))
{
throw new GameQException("The host '{$server_ip}' is unresolvable to an IP address.");
return FALSE;
}
}
}
// Create the class so we can reference it properly later
$protocol_class = 'GameQ_Protocols_'.ucfirst($server_info[self::SERVER_TYPE]);
// Create the new instance and add it to the servers list
$this->servers[$server_id] = new $protocol_class(
$server_ip,
$server_port,
array_merge($this->options, $server_info[self::SERVER_OPTIONS])
);
return $this; // Make calls chainable
}
/**
* Add multiple servers at once
*
* @param array $servers
* @return GameQ
*/
public function addServers(Array $servers=NULL)
{
// Loop thru all the servers and add them
foreach($servers AS $server_info)
{
$this->addServer($server_info);
}
return $this; // Make calls chainable
}
/**
* Clear all the added servers. Creates clean instance.
*
* @return GameQ
*/
public function clearServers()
{
// Reset all the servers
$this->servers = array();
$this->sockets = array();
return $this; // Make Chainable
}
/**
* Make all the data requests (i.e. challenges, queries, etc...)
*
* @return multitype:Ambigous <multitype:, multitype:boolean string mixed >
*/
public function requestData()
{
// Data returned array
$data = array();
// Init the query array
$queries = array(
'multi' => array(
'challenges' => array(),
'info' => array(),
),
'linear' => array(),
);
// Loop thru all of the servers added and categorize them
foreach($this->servers AS $server_id => $instance)
{
// Check to see what kind of server this is and how we can send packets
if($instance->packet_mode() == GameQ_Protocols::PACKET_MODE_LINEAR)
{
$queries['linear'][$server_id] = $instance;
}
else // We can send this out in a multi request
{
// Check to see if we should issue a challenge first
if($instance->hasChallenge())
{
$queries['multi']['challenges'][$server_id] = $instance;
}
// Add this instance to do info query
$queries['multi']['info'][$server_id] = $instance;
}
}
// First lets do the faster, multi queries
if(count($queries['multi']['info']) > 0)
{
$this->requestMulti($queries['multi']);
}
// Now lets do the slower linear queries.
if(count($queries['linear']) > 0)
{
$this->requestLinear($queries['linear']);
}
// Now let's loop the servers and process the response data
foreach($this->servers AS $server_id => $instance)
{
// Lets process this and filter
$data[$server_id] = $this->filterResponse($instance);
}
// Send back the data array, could be empty if nothing went to plan
return $data;
}
/* Working Methods */
/**
* Apply all set filters to the data returned by gameservers.
*
* @param GameQ_Protocols $protocol_instance
* @return array
*/
protected function filterResponse(GameQ_Protocols $protocol_instance)
{
// Let's pull out the "raw" data we are going to filter
$data = $protocol_instance->processResponse();
// Loop each of the filters we have attached
foreach($this->options['filters'] AS $filter_name => $filter_instance)
{
// Overwrite the data with the "filtered" data
$data = $filter_instance->filter($data, $protocol_instance);
}
return $data;
}
/**
* Process "linear" servers. Servers that do not support multiple packet calls at once. So Slow!
* This method also blocks the socket, you have been warned!!
*
* @param array $servers
* @return boolean
*/
protected function requestLinear($servers=array())
{
// Loop thru all the linear servers
foreach($servers AS $server_id => $instance)
{
// First we need to get a socket and we need to block because this is linear
if(($socket = $this->socket_open($instance, TRUE)) === FALSE)
{
// Skip it
continue;
}
// Socket id
$socket_id = (int) $socket;
// See if we have challenges to send off
if($instance->hasChallenge())
{
// Now send off the challenge packet
fwrite($socket, $instance->getPacket('challenge'));
// Read in the challenge response
$instance->challengeResponse(array(fread($socket, 4096)));
// Now we need to parse and apply the challenge response to all the packets that require it
$instance->challengeVerifyAndParse();
}
// Invoke the beforeSend method
$instance->beforeSend();
// Grab the packets we need to send, minus the challenge packet
$packets = $instance->getPacket('!challenge');
// Now loop the packets, begin the slowness
foreach($packets AS $packet_type => $packet)
{
// Add the socket information so we can retreive it easily
$this->sockets = array(
$socket_id => array(
'server_id' => $server_id,
'packet_type' => $packet_type,
'socket' => $socket,
)
);
// Write the packet
fwrite($socket, $packet);
// Get the responses from the query
$responses = $this->sockets_listen();
// Lets look at our responses
foreach($responses AS $socket_id => $response)
{
// Save the response from this packet
$instance->packetResponse($packet_type, $response);
}
}
}
// Now close all the socket(s) and clean up any data
$this->sockets_close();
return TRUE;
}
/**
* Process the servers that support multi requests. That means multiple packets can be sent out at once.
*
* @param array $servers
* @return boolean
*/
protected function requestMulti($servers=array())
{
// See if we have any challenges to send off
if(count($servers['challenges']) > 0)
{
// Now lets send off all the challenges
$this->sendChallenge($servers['challenges']);
// Now let's process the challenges
// Loop thru all the instances
foreach($servers['challenges'] AS $server_id => $instance)
{
$instance->challengeVerifyAndParse();
}
}
// Send out all the query packets to get data for
$this->queryServerInfo($servers['info']);
return TRUE;
}
/**
* Send off needed challenges and get the response
*
* @param array $instances
* @return boolean
*/
protected function sendChallenge(Array $instances=NULL)
{
// Loop thru all the instances we need to send out challenges for
foreach($instances AS $server_id => $instance)
{
// Make a new socket
if(($socket = $this->socket_open($instance)) === FALSE)
{
// Skip it
continue;
}
// Now write the challenge packet to the socket.
fwrite($socket, $instance->getPacket(GameQ_Protocols::PACKET_CHALLENGE));
// Add the socket information so we can retreive it easily
$this->sockets[(int) $socket] = array(
'server_id' => $server_id,
'packet_type' => GameQ_Protocols::PACKET_CHALLENGE,
'socket' => $socket,
);
// Let's sleep shortly so we are not hammering out calls rapid fire style hogging cpu
usleep($this->write_wait);
}
// Now we need to listen for challenge response(s)
$responses = $this->sockets_listen();
// Lets look at our responses
foreach($responses AS $socket_id => $response)
{
// Back out the server_id we need to update the challenge response for
$server_id = $this->sockets[$socket_id]['server_id'];
// Now set the proper response for the challenge because we will need it later
$this->servers[$server_id]->challengeResponse($response);
}
// Now close all the socket(s) and clean up any data
$this->sockets_close();
return TRUE;
}
/**
* Query the server for actual server information (i.e. info, players, rules, etc...)
*
* @param array $instances
* @return boolean
*/
protected function queryServerInfo(Array $instances=NULL)
{
// Loop all the server instances
foreach($instances AS $server_id => $instance)
{
// Invoke the beforeSend method
$instance->beforeSend();
// Get all the non-challenge packets we need to send
$packets = $instance->getPacket('!challenge');
if(count($packets) == 0)
{
// Skip nothing else to do for some reason.
continue;
}
// Now lets send off the packets
foreach($packets AS $packet_type => $packet)
{
// Make a new socket
if(($socket = $this->socket_open($instance)) === FALSE)
{
// Skip it
continue;
}
// Now write the packet to the socket.
fwrite($socket, $packet);
// Add the socket information so we can retreive it easily
$this->sockets[(int) $socket] = array(
'server_id' => $server_id,
'packet_type' => $packet_type,
'socket' => $socket,
);
// Let's sleep shortly so we are not hammering out calls raipd fire style
usleep($this->write_wait);
}
}
// Now we need to listen for packet response(s)
$responses = $this->sockets_listen();
// Lets look at our responses
foreach($responses AS $socket_id => $response)
{
// Back out the server_id
$server_id = $this->sockets[$socket_id]['server_id'];
// Back out the packet type
$packet_type = $this->sockets[$socket_id]['packet_type'];
// Save the response from this packet
$this->servers[$server_id]->packetResponse($packet_type, $response);
}
// Now close all the socket(s) and clean up any data
$this->sockets_close();
return TRUE;
}
/* Sockets/streams stuff */
/**
* Open a new socket based on the instance information
*
* @param GameQ_Protocols $instance
* @param bool $blocking
* @throws GameQException
* @return boolean|resource
*/
protected function socket_open(GameQ_Protocols $instance, $blocking=FALSE)
{
// Create the remote address
$remote_addr = sprintf("%s://%s:%d", $instance->transport(), $instance->ip(), $instance->port());
// Create context
$context = stream_context_create(array(
'socket' => array(
'bindto' => '0:0', // Bind to any available IP and OS decided port
),
));
// Create the socket
if(($socket = @stream_socket_client($remote_addr, $errno = NULL, $errstr = NULL, $this->timeout, STREAM_CLIENT_CONNECT, $context)) !== FALSE)
{
// Set the read timeout on the streams
stream_set_timeout($socket, $this->timeout);
// Set blocking mode
stream_set_blocking($socket, $blocking);
}
else // Throw an error
{
// Check to see if we are in debug mode, if so throw the exception
if($this->debug)
{
throw new GameQException(__METHOD__." Error creating socket to server {$remote_addr}. Error: ".$errstr, $errno);
}
// We didnt create so we need to return false.
return FALSE;
}
unset($context, $remote_addr);
// return the socket
return $socket;
}
/**
* Listen to all the created sockets and return the responses
*
* @return array
*/
protected function sockets_listen()
{
// Set the loop to active
$loop_active = TRUE;
// To store the responses
$responses = array();
// To store the sockets
$sockets = array();
// Loop and pull out all the actual sockets we need to listen on
foreach($this->sockets AS $socket_id => $socket_data)
{
// Append the actual socket we are listening to
$sockets[$socket_id] = $socket_data['socket'];
}
// Init some variables
$read = $sockets;
$write = NULL;
$except = NULL;
// This is when it should stop
$time_stop = microtime(TRUE) + $this->timeout;
// Let's loop until we break something.
while ($loop_active && microtime(TRUE) < $time_stop)
{
// Now lets listen for some streams, but do not cross the streams!
$streams = stream_select($read, $write, $except, 0, $this->stream_timeout);
// We had error or no streams left, kill the loop
if($streams === FALSE || ($streams <= 0))
{
$loop_active = FALSE;
break;
}
// Loop the sockets that received data back
foreach($read AS $socket)
{
// See if we have a response
if(($response = stream_socket_recvfrom($socket, 8192)) === FALSE)
{
continue; // No response yet so lets continue.
}
// Check to see if the response is empty, if so we are done
// @todo: Verify that this does not affect other protocols, added for Minequery
// Initial testing showed this change did not affect any of the other protocols
if(strlen($response) == 0)
{
// End the while loop
$loop_active = FALSE;
break;
}
// Add the response we got back
$responses[(int) $socket][] = $response;
}
// Because stream_select modifies read we need to reset it each
// time to the original array of sockets
$read = $sockets;
}
// Free up some memory
unset($streams, $read, $write, $except, $sockets, $time_stop, $response);
return $responses;
}
/**
* Close all the open sockets
*/
protected function sockets_close()
{
// Loop all the existing sockets, valid or not
foreach($this->sockets AS $socket_id => $data)
{
fclose($data['socket']);
unset($this->sockets[$socket_id]);
}
return TRUE;
}
}
/**
* GameQ Exception Class
*
* Thrown when there is any kind of internal configuration error or
* some unhandled or unexpected error or response.
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQException extends Exception {}

674
web/third_party/gameq/LICENSE vendored Normal file
View File

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

378
web/third_party/gameq/gameq/buffer.php vendored Normal file
View File

@ -0,0 +1,378 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
/**
* Provide an interface for easy manipulation of a server response
*
* @author Aidan Lister <aidan@php.net>
* @author Tom Buskens <t.buskens@deviation.nl>
* @version $Revision: 1.4 $
*/
class GameQ_Buffer
{
/**
* The original data
*
* @var string
* @access public
*/
private $data;
/**
* The original data
*
* @var string
* @access public
*/
private $length;
/**
* Position of pointer
*
* @var string
* @access public
*/
private $index = 0;
/**
* Constructor
*
* @param string|array $response The data
*/
public function __construct($data)
{
$this->data = $data;
$this->length = strlen($data);
}
/**
* Return all the data
*
* @return string|array The data
*/
public function getData()
{
return $this->data;
}
/**
* Return data currently in the buffer
*
* @return string|array The data currently in the buffer
*/
public function getBuffer()
{
return substr($this->data, $this->index);
}
/**
* Returns the number of bytes in the buffer
*
* @return int Length of the buffer
*/
public function getLength()
{
return max($this->length - $this->index, 0);
}
/**
* Read from the buffer
*
* @param int $length Length of data to read
* @return string The data read
*/
public function read($length = 1)
{
if (($length + $this->index) > $this->length) {
throw new GameQ_ProtocolsException('Unable to read length={$length} from buffer. Bad protocol format or return?');
}
$string = substr($this->data, $this->index, $length);
$this->index += $length;
return $string;
}
/**
* Read the last character from the buffer
*
* Unlike the other read functions, this function actually removes
* the character from the buffer.
*
* @return string The data read
*/
public function readLast()
{
$len = strlen($this->data);
$string = $this->data{strlen($this->data) - 1};
$this->data = substr($this->data, 0, $len - 1);
$this->length -= 1;
return $string;
}
/**
* Look at the buffer, but don't remove
*
* @param int $length Length of data to read
* @return string The data read
*/
public function lookAhead($length = 1)
{
$string = substr($this->data, $this->index, $length);
return $string;
}
/**
* Skip forward in the buffer
*
* @param int $length Length of data to skip
* @return void
*/
public function skip($length = 1)
{
$this->index += $length;
}
/**
* Jump to a specific position in the buffer,
* will not jump past end of buffer
*
* @param int $index Position to go to
* @return void
*/
public function jumpto($index)
{
$this->index = min($index, $this->length - 1);
}
/**
* Get the current pointer position
*
* @return int The current pointer position
*/
public function getPosition()
{
return $this->index;
}
/**
* Read from buffer until delimiter is reached
*
* If not found, return everything
*
* @param string $delim Read until this character is reached
* @return string The data read
*/
public function readString($delim = "\x00")
{
// Get position of delimiter
$len = strpos($this->data, $delim, min($this->index, $this->length));
// If it is not found then return whole buffer
if ($len === false) {
return $this->read(strlen($this->data) - $this->index);
}
// Read the string and remove the delimiter
$string = $this->read($len - $this->index);
++$this->index;
return $string;
}
/**
* Reads a pascal string from the buffer
*
* @paran int $offset Number of bits to cut off the end
* @param bool $read_offset True if the data after the offset is
* to be read
* @return string The data read
*/
public function readPascalString($offset = 0, $read_offset = false)
{
// Get the proper offset
$len = $this->readInt8();
$offset = max($len - $offset, 0);
// Read the data
if ($read_offset) {
return $this->read($offset);
}
else {
return substr($this->read($len), 0, $offset);
}
}
/**
* Read from buffer until any of the delimiters is reached
*
* If not found, return everything
*
* @param array $delims Read until these characters are reached
* @return string The data read
*/
public function readStringMulti($delims, &$delimfound = null)
{
// Get position of delimiters
$pos = array();
foreach ($delims as $delim) {
if ($p = strpos($this->data, $delim, min($this->index, $this->length))) {
$pos[] = $p;
}
}
// If none are found then return whole buffer
if (empty($pos)) {
return $this->read(strlen($this->data) - $this->index);
}
// Read the string and remove the delimiter
sort($pos);
$string = $this->read($pos[0] - $this->index);
$delimfound = $this->read();
return $string;
}
/**
* Read a 32-bit unsigned integer
*/
public function readInt32()
{
$int = unpack('Lint', $this->read(4));
return $int['int'];
}
/**
* Read a 32-bit signed integer
*/
public function readInt32Signed()
{
$int = unpack('lint', $this->read(4));
return $int['int'];
}
/**
* Read a 16-bit unsigned integer
*/
public function readInt16()
{
$int = unpack('Sint', $this->read(2));
return $int['int'];
}
/**
* Read a 16-big signed integer
*/
public function readInt16Signed()
{
$int = unpack('sint', $this->read(2));
return $int['int'];
}
/**
* Read an int8 from the buffer
*
* @return int The data read
*/
public function readInt8()
{
return ord($this->read(1));
}
/**
* Read an float32 from the buffer
*
* @return int The data read
*/
public function readFloat32()
{
$float = unpack('ffloat', $this->read(4));
return $float['float'];
}
/**
* Conversion to float
*
* @access public
* @param string $string String to convert
* @return float 32 bit float
*/
public function toFloat($string)
{
// Check length
if (strlen($string) !== 4) {
return false;
}
// Convert
$float = unpack('ffloat', $string);
return $float['float'];
}
/**
* Conversion to integer
*
* @access public
* @param string $string String to convert
* @param int $bits Number of bits
* @return int Integer according to type
*/
public function toInt($string, $bits = 8)
{
// Check length
if (strlen($string) !== ($bits / 8)) {
return false;
}
// Convert
switch($bits) {
// 8 bit unsigned
case 8:
$int = ord($string);
break;
// 16 bit unsigned
case 16:
$int = unpack('Sint', $string);
$int = $int['int'];
break;
// 32 bit unsigned
case 32:
$int = unpack('Lint', $string);
$int = $int['int'];
break;
// Invalid type
default:
$int = false;
break;
}
return $int;
}
}

34
web/third_party/gameq/gameq/filters.php vendored Normal file
View File

@ -0,0 +1,34 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Generic function to make extending shorter
*
* @author Austin Bischoff <austin@codebeard.com>
*/
abstract class GameQ_Filters extends GameQ_Filters_Core {}
/**
* GameQ Filters Exception
*
* Allows for a level of exception handling incase there is an issue/error within
* a filter or a required dependency has not been met.
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_FiltersException extends Exception {}

View File

@ -0,0 +1,55 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Abstract class which all filters must inherit
*
* @author Austin Bischoff <austin@codebeard.com>
*/
abstract class GameQ_Filters_Core
{
protected $params = array();
/**
* Constructor, receives parameters
*
* @param array $params Filter parameters
*/
function __construct($params)
{
if(is_array($params))
{
foreach ($params as $key => $param)
{
$this->params[$key] = $param;
}
}
else
{
$this->params = $params;
}
}
/**
* Actually apply the filter to the passed results
*
* @param array $results
* @param GameQ_Protocols_Core $protocol_instance
*/
abstract public function filter($results, GameQ_Protocols_Core $protocol_instance);
}

View File

@ -0,0 +1,193 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* This filter makes sure a fixed set of properties (i.e. gq_) is always available regardless of protocol
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Filters_Normalise extends GameQ_Filters
{
/**
* Default normalization items. Can be overwritten on a protocol basis.
*
* @var array
*/
protected $normalize = array(
// General
'general' => array(
// target => source
'dedicated' => array('listenserver', 'dedic', 'bf2dedicated', 'netserverdedicated', 'bf2142dedicated'),
'gametype' => array('ggametype', 'sigametype', 'matchtype'),
'hostname' => array('svhostname', 'servername', 'siname', 'name'),
'mapname' => array('map', 'simap'),
'maxplayers' => array('svmaxclients', 'simaxplayers', 'maxclients'),
'mod' => array('game', 'gamedir', 'gamevariant'),
'numplayers' => array('clients', 'sinumplayers'),
'password' => array('protected', 'siusepass', 'sineedpass', 'pswrd', 'gneedpass', 'auth'),
'players' => array('players'),
'teams' => array('team'),
),
// Indvidual
'player' => array(
'name' => array('nick', 'player', 'playername', 'name'),
'kills' => array('kills'),
'deaths' => array('deaths'),
'score' => array('kills', 'frags', 'skill', 'score'),
'ping' => array('ping'),
),
// Team
'team' => array(
'name' => array('name', 'teamname', 'team_t'),
'score' => array('score', 'score_t'),
),
);
/**
* Normalize the server data
* @see GameQ_Filters_Core::filter()
*/
public function filter($data, GameQ_Protocols_Core $protocol_instance)
{
$result = array();
// No data passed so something bad happened
if(empty($data))
{
return $result;
}
// Here we check to see if we override these defaults.
if(($normalize = $protocol_instance->getNormalize()) !== FALSE)
{
// Merge this stuff in
$this->normalize = array_merge_recursive($this->normalize, $normalize);
}
// normalize the general items
$result = $this->normalize($data, 'general');
// normalize players
if (isset($result['gq_players']) && is_array($result['gq_players']))
{
// Don't rename the players array
$result['players'] = $result['gq_players'];
foreach ($result['players'] as $key => $player)
{
$result['players'][$key] = array_merge($player, $this->normalize($player, 'player'));
}
$result['gq_numplayers'] = count($result['players']);
}
else
{
$result['players'] = array();
}
// normalize teams
if (isset($result['gq_teams']) && is_array($result['gq_teams']))
{
// Don't rename the teams array
$result['teams'] = $result['gq_teams'];
foreach ($result['teams'] as $key => $team)
{
$result['teams'][$key] = array_merge($team, $this->normalize($team, 'team'));
}
$result['gq_numteams'] = count($result['teams']);
}
else
{
$result['teams'] = array();
}
unset($result['gq_players'], $result['gq_teams']);
// Merge and sort array
$result = (array_merge($data, $result));
ksort($result);
return $result;
}
/**
* normalize an array
*
* @param array $data The data to normalize
* @param array $properties The properties we want to normalize
* @return array A normalized array
*/
private function normalize($data, $properties)
{
// Make sure this is not empty
if(!isset($this->normalize[$properties]))
{
// We just return empty array
return array();
}
$props = $this->normalize[$properties];
// Create a new array, with all the specified variables
$new = $this->fill($props);
foreach ($data as $var => $value)
{
// normalize values
$stripped = strtolower(str_replace('_', '', $var));
foreach ($props as $target => $sources)
{
if ($target == $stripped or in_array($stripped, $sources))
{
$new['gq_' . $target] = $value;
//unset($vars[$target]);
break;
}
}
}
return $new;
}
/**
* Fill array with array keys
*
* @param array $vars The array keys
* @param mixed $val Value of each key
* @return array An array filled with keys
*/
private function fill($vars, $val = false)
{
$data = array();
foreach ($vars as $target => $source)
{
$data['gq_' . $target] = $val;
}
return $data;
}
}

View File

@ -0,0 +1,76 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Strip color codes from specific protocol types. This code was adapted from the original filter class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Filters_Stripcolor extends GameQ_Filters
{
/**
* Strip all the color junk from returns
* @see GameQ_Filters_Core::filter()
*/
public function filter($data, GameQ_Protocols_Core $protocol_instance)
{
// Check the type of protocol
switch($protocol_instance->protocol())
{
case 'quake2':
case 'quake3':
case 'doom3':
array_walk_recursive($data, array($this, 'stripQuake'));
break;
case 'unreal2':
case 'ut3':
case 'gamespy3': //not sure if gamespy3 supports ut colors but won't hurt
case 'gamespy2':
array_walk_recursive($data, array($this, 'stripUT'));
break;
default:
break;
}
return $data;
}
/**
* Strips quake color tags
*
* @param $string string String to strip
* @param $key string Array key
*/
protected function stripQuake(&$string, $key)
{
$string = preg_replace('#(\^.)#', '', $string);
}
/**
* Strip UT color tags
*
* @param $string string String to strip
* @param $key string Array key
*/
protected function stripUT(&$string, $key)
{
$string = preg_replace('/\x1b.../', '', $string);
}
}

View File

@ -0,0 +1,37 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Generic function to make extending shorter
*
* @author Austin Bischoff <austin@codebeard.com>
*/
abstract class GameQ_Protocols extends GameQ_Protocols_Core
{
}
/**
* GameQ Protocol Exception
*
* Allows for another level of exception handling when doing loops. Makes it possible to recover and continue
* when there is an exception within one of the protocol classes.
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_ProtocolsException extends Exception {}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* America's Army 1/2 Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Aa extends GameQ_Protocols_Gamespy2
{
protected $name = "aa";
protected $name_long = "America's Army";
protected $port = 1717;
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* America's Army 3 Protocol Class (Version 3.2+)
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Aa3 extends GameQ_Protocols_Source
{
protected $name = "aa3";
protected $name_long = " America's Army 3 (> 3.2)";
protected $port = 27020;
}

View File

@ -0,0 +1,425 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* America's Army 3 Protocol Class (Version < 3.2)
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Aa3pre32 extends GameQ_Protocols
{
/**
* This class is no longer valid
*
* @var int
*/
protected $state = self::STATE_DEPRECATED;
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @var array
*/
protected $packets = array(
self::PACKET_ALL => "\x4A\x35\xFF\xFF\x02\x00\x02\x00\x01\x00%s",
);
/**
* Methods to be run when processing the response(s)
*
* @var array
*/
protected $process_methods = array(
"process_all",
);
/**
* Default port for this server type
*
* @var int
*/
protected $port = 39300; // Default port, used if not set when instanced
/**
* The protocol being used
*
* @var string
*/
protected $protocol = 'aa3pre32';
/**
* String name of this protocol class
*
* @var string
*/
protected $name = 'aa3pre32';
/**
* Longer string name of this protocol class
*
* @var string
*/
protected $name_long = "America's Army 3 (< 3.2)";
/*
* Internal methods
*/
/**
* Called before the $this->packets are sent.
*
* @see GameQ_Protocols_Core::beforeSend()
*/
public function beforeSend()
{
// Encrypt the data we want to send
$enc_data = $this->ssc_crypt("\x0A\x00playerName\x06\x06\x00query\x00", TRUE);
// Apply this to the packet
$this->packets[self::PACKET_ALL] = sprintf($this->packets[self::PACKET_ALL], $enc_data);
return TRUE;
}
protected function preProcess_all($packets=array())
{
// Check to make sure we have zlib installed
if(!function_exists('gzuncompress'))
{
throw new GameQ_ProtocolsException('Zlib is not installed. See http://www.php.net/manual/en/book.zlib.php for more info.', 0);
return FALSE;
}
// We only got one packet
if(count($packets) == 1)
{
// @todo: Looking for example to test and verify
$packets[0] = substr($packets[0], 10);
}
else // Multiple Packets
{
$packets_sorted = array();
// We need to sort the packets to make sure they are in the proper order
foreach($packets AS $packet)
{
$packets_sorted[ord($packet[10])] = substr($packet, 14);
}
// Key sort the packets
ksort($packets_sorted);
$packets = $packets_sorted;
unset($packet, $packets_sorted);
}
// Merge all the packets and decypt the data
$data = $this->ssc_crypt(trim(implode("", $packets)), FALSE);
// Decompress and return
return gzuncompress($data);
}
protected function process_all()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_ALL))
{
return array();
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Let's preprocess the rules
$data = $this->preProcess_all($this->packets_response[self::PACKET_ALL]);
// Lets parse out all the data
if(preg_match('/attributeNames(.+)attributeValues(.+)resultCode(.*)/ism', $data, $m) === FALSE)
{
throw new GameQ_ProtocolsException("AA3 Packet response is not in a valid format");
return array();
}
// Init temp array
$tmp = array(
"keys" => array(),
"values" => array(),
);
// Pull the array into named vars
list($all, $keys, $values, $resultcode) = $m;
// Lets look at all the keys
$buf = new GameQ_Buffer($keys);
// Skip
$buf->skip(4);
while($buf->getLength())
{
// Pull out the string and strip out any junk
$str = preg_replace('/[^[:alnum:][:punct:]\s]/', '', trim($buf->readString(), "\x00..\x1F"));
// Do not continue on empty strings
if(strlen($str) == 0)
{
continue;
}
// Add to the temp list. We will clean this up later
$tmp['keys'][] = $str;
}
// Now lets loop the values
$buf = new GameQ_Buffer($values);
// Skip
$buf->skip(4);
while($buf->getLength())
{
$str = preg_replace('/[^[:alnum:][:punct:]\s]/', '', trim($buf->readString(), "\x00..\x1F"));
// Do not continue on empty strings
if(strlen($str) == 0)
{
continue;
}
// Add to the temp list. We will clean this up later
$tmp['values'][] = $str;
}
// Combine the keys and values
$tmp = array_combine($tmp['keys'], $tmp['values']);
$teams = array();
$team = FALSE;
// Let's parse the combined array and make the result.
foreach($tmp AS $key => $value)
{
// Is player
if(preg_match('/^player(\D+)(\d+)$/', $key, $matches))
{
$result->addPlayer($matches[1], $value);
// See if this is a team value
if($matches[1] == 'Team')
{
$team = $value;
}
elseif ($matches[1] == 'TeamIndex' && !array_key_exists($value, $teams))
{
$teams[$value] = $team;
}
}
else // Is server var
{
$result->add(substr($key, 6), $value);
}
}
// Do the teams
foreach ($teams AS $teamIndex => $teamName)
{
$result->addTeam('name', $teamName);
$result->addTeam('index', $teamIndex);
}
unset($buf, $tmp, $data, $keys, $values, $resultcode, $matches, $teams);
return $result->fetch();
}
/**
* encrypt|decrypt the buffer.
*
* @param string $buffer
* @param bool $encrypt
*/
protected function ssc_crypt($buffer, $encrypt = FALSE)
{
$master_key = pack("H*",
"f5c5914b27235dc0dc274200ddd187c32fe02aed5fc5c079518f49208e4c5548aaef313c5d2e7c91dc580d3cd9e1aec577595325d3c5c84b44a020802becb17e".
"7d6b6b87e8a4ebc8e4cafbaf5720f9600818b334ad2695ba0f19e1fbd48d0139f05e9059e98a15c79ebabb4f3aa8039d8720aef2bf1b4693a67a20a114b8505b".
"693cf5b24a236503582ecdb8109a7d89a8d90d660b96435b4656ecec3fff2086e94c54988843d2aa55adefb2d47fc804c0024a7897e993b2326e8990e425f7c8".
"38aef55f2002f22d84479f43849de260a8a2de6a7de09225c275a172729e65be687182bde68cb17b3fd77bf513c8045f0b6696d3a501b255db0632e36c0e7806".
"c5c193b5b9a9c621f0ac9a0ee72196edbb336e7431b75eba95d02191048ab7c3874578218d79a2623e308184fdac98a1568c09b8907d8411e29c53823a3a68bc".
"c785547ebb29401822da7fa59c6fc412cf2a9201f31336bcdffe78501058b1d7814e920ceee7aca8fa798f10f0a8ba19a1deae864e1c77f974880e5571a4380b".
"52d3357ec8cbf8ff6ff7e8f3fa6223f923e4a7bb1918054bcd2a115e466307f39d964c051983f8b2e5db0b39332ec08c94d9b36a4594ab5e868bc888e4586687".
"b6e62b2bb06ad0903544e379d744896f95346a0238b2b72c6d38ed1bf011185bad1910812cfe2c5b38db10433088f2e5a3746e7302467d35e8f07722fad1f7d4".
"283fbea23fa6f50f710491b1f0a8dd3a187939e7f344de57c256ffb063791fc556d3791570a873537c3f05f8ca08aa1eb2e3f641e0fb46fde7394f8fb4c216d7".
"55c020b405a21b8e4340136fc9583800afd87a677d3d9b6b95585ba502d6db2dec504f25b612340e29be64700682f4f012908e2672916ba83d35deb58d826d83".
"d75a61f726876747d78df10a31f6acb36cb64dec47b7da11c7e7177dcc097965a50065e8e5f91732e20647604c00c0fa451f7ee140d93515b7b5e6f9e0c92ad0".
"29648ab1e0ea363c5a19d12832c54c0ae67baa7e029217ede5f97cd07ebf3aaf14c020f4646e3792e2472409299868b9ee1ce7a69a30203218289523d848a2ee".
"42b96edf05f24182491dfb048c17f815aa8983d9ab72723defbe9750cd694bc1318c92862ed7b7ab1e37472b986a7f4745224fd723e4e6ef53ff6d5f51f1b8cd".
"34b32b9ac92968e5ec8b631aa750e7cec51e7fddca5da1cdc836c0243ab2a2f86d072479c117738fafba4d72db6fee13274d652a7c76ff962c1389b32f95f3c0".
"04d178b71646fe084507e7dd4b4db98405cb72399f78f989c188fb2ed6e18e5aa417adae504d33ad8414f9e3a6e466837062e8ea91664f63134539679b119d6b".
"3918f833ceddc249933b0ae83e0965b38fb86d3da02622d02f57c7282e5f0cdb18f71e7450c538ddca55588575f80754dd0c89840bcf7e246e8f041309069f15".
"a49c27fa0a5913c72be881ae27ff6b0332701d96dc295576d2a9bc0fd266f5604da647f78d1c2ced95c4cf8a929c55bf524198898b444c67040d7c7debcc3cc9".
"7cab1a8fe190f4db097beaccea9a34e38380b43bd2b2bf98f471c02894aaaf3944680988497aa74d293238d503a4df19d90af204fdcbb1875170a96b7f3e288c".
"0f24e1c8b9ce4f77f2b03944c2abbacba69331a244923c38f731f368d10eca82dd503bdece016064c68cb38a4e3408712959cb5216dc42bf5365eb789c484bcc".
"5813a1f1680fc5606e8da06bd5a68a73bd593fcd4aeb9aca06bb258f84a38dd0d4c6c0c355c4d5e0e1a97abaa11869f26285a99db4dfb8eab0b0f53e80d2486b".
"9a6cc63affac0b830b12434ddbc1c4ef3ee46af67fcc711b88a352d2b324c0acfb35bfbe74865afd7f3293a944cd9f69230a206c5112ed9858497ddc118c0338".
"63f1a974b033a225c74e83c9d1bec1a3e6a7b2b7ddab58aec40fe4bed9e2fd1beaded608c695dafaf4d683fdf3b9175d1283d7d99b47c40209a555c317e29bad".
"574ac49e78ae91896b527d27f04d89b10d5f754b953d1218bf01fc06086c031ff334eab692e9c6fb221ac0f3027283ac5350d860f2d6125d31edf4b7ac806f21".
"abeb04f84230e8c17455e54a27d6862cfb3279370eae1cdb1f84c10209e89241182c307b45a6b97520a62bc263c66f78d27b52ad9728f5d78c1626297b1d1cdd".
"e47fd67d9f1f4846a3643810359f2cc6b22a662683836eb48f6e1605be3a830fe29f0c54412e7d82aefff9748a2fddb368dd0103161e2a17da69216e22adf6b5".
"7ce255e400279188655820eedd5a1935aa3d8cf621fa312bab89cbb3071bfbe7e0635126de8217bd5c342f35824511769ac6b72de09b87012cd85f2cbef53e11".
"9aba484771b15bddda183501230ae6a16fcde55a161df16f178e04478a3711437dc91eeabe92e14b44d2f49036532be42c425346df9d91288aa409a63272e061".
"baaaca491cc04c44b2ac739290baa76d9fdc7b66733548af6411a6ba790c4962ddf033e63fab462bc0ccbfa45d45ce377d32f4c7e905cab5fbbb524f8c2907d0".
"41b304d1f38f348efd34a7d51c118445d05353b5f0449f368450782df457ca55169bdfa817a94e1082faf4115cf3d6d890481affb2feb95145691f152485465d".
"0f8dba4cde2079784574fadbe805222e3a132934f1a419cda032b310fd7dfa2830d3f3385d646ba0c373cba4d624a6267300014cdd2dd5e87999aa5b0e5df0a8".
"de50f3473918474ccf82f9c8ab9f31379a9d8d00bead3bc8b9d00f4ebba9c7b0ea882454e3a785e096d7887b3a507f089dba88925df12c633241ed2f9f68905b".
"66775d1d0ca3cc312f7be8641856be8de24248e55dd737df8410e23e9457024f534261f09ab278821b1c89da824f7f546a4163f4d53ccf07ee9bd59adb673822".
"87092b94a7847141a796a6abf90f7bfa5d8967bfba2275283863bfc3f8283f0e5b223748a55dff04f3c6bf228bb1e0bfd2c80289abf5819e165268b4e687bcf4".
"a33f1c42c47a6236ca14c26778ad2cbe013c20807e45276d49a4e0df7df7c42d2c73f298f61fc8e778ba953a71c6b7d1779624552df0f3896a790671a3a981fa".
"17914d856321d0997ff4b2d05944335ceac60b63b1d827eab5ef7483990e9bd1b5453a473e1efd476ba1e093466cb21dc72e35dc12bb8c8d3bb29db420251590".
"32441b8a7e9458cad9cdc1551ce52312bb27d858a8ae319e525b38f20242a60933b2a21bd858e147cc6ee702983c84bf535d1575a54dc46c03cdb42a39d1a64e".
"433d9bea41f9915f7d9d462d4308baccb19bb1adc3e0125715950f7c7f8b54312826204fd512386da587bad7bf81069dc554fd8fd77153832225e56a7fa4046b".
"d588ed258dc7e54ccf1c021f9800376376bdcfc62116555ab0e06b3161b3b7a6a7a87de2371215207c43fce54c82feddc5d444b08f6a30c0095007d526da1b02".
"41563a9360f86ef3b824294bd174679f4dee74912acdeb00ac96a713ad86dc212a544b7420fa6c83d5dec48400e1f11f8163e20c932bc893820a8261939e0f85".
"fdb416c6a0a18cc0182d675702a8362694f23ce686962150f862357fe84a0b572068c7e0578909d7f82c87cd17e7ef50e5566eab694ac76edb4b6d8a85cd2910".
"0b93272b0a524a24db8db7d4622fae63d982e4090fb519e30736d5b5152d58a234919d216d0294628841cba91ed72d985ba92f7cc548378e7ddf812816ad99dd".
"27adffdf5b6d762a79a942d8af9a8f0ac81afc98869dcdcc06835478947ced5ccbb22d02624e207c774042fa8c133221c362bef69582c52ca9c014db1ec2d351".
"a1d72bb01c06e32ca0a4ecfe923737f0f7145b27c943a9be1f174dd46d3af58e7a2f612177affd11ae7e1b9231aadb46bcb732ee79de7e62f467721f06d8e9e5".
"59b526bb702ddbc0f0b46a2162458c15c0154cbb1b1edad3fa198a0781279ecc5e5391269c335bc94b2f21da781cf943cd0e700206128fe1f1e3af4e70bfbaec".
"1c7ae4884c7e7544050036b001f87fc2f10762888701c160010e7691ea2b53b646d22178ebf1a56eb9cba86ffa2b570d846e231037d403298103c61732b04113".
"ff7ec74e0a671332f7df9da231f995c1fb53523c17c23105312b7d8ab63e5f6a0e7b9d106f3ce575d14befb3a5803aabcc9edb5f1ddf9dcabff4efbd785b169d".
"f7fb1b991faf63f064b5fc8f2c7fcac4b35a61f19c92dec36a6aadf02dc3942dde51d7225aefeaf6b7527183c2adc832c6bc8735bc7be2c18ad3d70653f91581".
"ce42a275ef6715932ae7513d0ecb726be54c167cc89445a08cb8e12fc583aee815b3947bd1ac781fcbfbdda25fe3e931a21c47058197ceffbe9bd2ac6394b2d5".
"95c3e10076c3aceba33b1556029edfbc04849e0d66713f7beeb1517dcd43279a5073ec9fa221bfaceef0f639e771a44156778cbb696af28e2437eea3fc025d27".
"70b1409d978e4ec808c58288d525ac977db0ace80d9554925bf8767b8e91a9bf1ed25deabdbb93315ca08f711ae3f768a911eeacd93bfa6db3957da83c0fd945".
"a7e596b66530aa7347e04590fd31db6b49485a9ea8208c0aab4068f482b185aaed6ee69e32f9ff7b882763da34f6e3bce94c79353ef6849d47e6345d8727e076".
"f1aa0133c2399e4d777525fe9aa29e75d23df6e829f9058580413d5c24f85568beb1343430f393adee28ab54e220b4c884fa6ebc2825705f863ba7d82977f653".
"edb2088abd84ad52a1810a52abc6e7c3b5687f3bf4744941ce48c876205f2497b641e6e4bb565ab816425c348e1f034104efda9a21723b00cdadc6ed2af6b225".
"524ae512afba6bc19c471e14bbba042dba641424005a816f25aee44ee84cf2f729b79b1b9d58218f0274d92168c9bb1cd1c141b5f8341a3a4dc78c0ddf08dfd4".
"110b4eb0b71b265fe70aa5a4b2186cafad5ff94dafd5b4b4560bac45cb47c4c863274ac2d84af46b75bfde496d39984ff0af8ab7d98bc12c02ce782b23268d03".
"864826b0201d8d1e0c09c9ab229a2f7fe1504795bafa8b8ae13fb046a2f35233a49b772b57862ada835951742439693ed9f3a080aea7a1309de4ae04b1ce3d78".
"72cdd85a3544906afaf55aff8255bdb2367c7ecf184c91c8f4c60a1301b80f8bb9f0ff6d80ac6e1c9d6c9fafbc65199790e0a9c323e68b105f5c56eed2f60294".
"5ab59d79698829ba092cc97f37dd023595d3fa014e718cda23d6bdbbfd70c2c6cc1b9121d22eae0bde7b94277dc8e5e096d60351f2740ddb986c7e10e0af8a40".
"e9bd526f863cde028dd253e18013d3c76c2006a9ab9ec3e7b6b1aca865b2ace8c8debb50ae1efbc0e49dd69f128c28bd02d79f22717e2679d5142540733cb278".
"0969944106122d5f2baf97f7e09ef67b894cd191411126ad962e4b9c5a0bbe83215563662ce5f063ce2a76c2e09613539fbb094d389e739ca0a3fc34bd1692ba".
"f0601e2122a70fdf68ede6c431090896622362c59801000727718f4b551f32340fc5f740e15fc0a023791aa57a6cc97af3077f5d71d33cbc864049b30cb11ea5".
"23c15141ea5ac620aec5f81e6661bf8f01a3c817ac1ab592570b63764402e4934d776df03cadae448c5d9082c30c00737e4bbe5c184a1167507d9b99bdd05592".
"456ac25dadb5beafe282028611db969c44db7bfb2cad349c0ecbebc281a00ad4f70cfd889b3533833ab845f86403e6a1970da6b5c8b8e82e9f42a82c7c14e535".
"16b3d9efbaae6ca6b9c93977f17f58ec29a1a8bb188fb15f377bf50d37e84781ca1716052f657a361cbe44eb227002a57390873e54b8695f76fe0f84f873e021".
"c92945f3d7b54861be3c237701c140c3a4e1b84fa4bab910cd265393e0172293d6fc40fa1872e175d7d3f06153a9eca3f8db85c2166f68415eda3bf4aee35adc".
"0231cd6cfe5d3a23b51fb0105176b9cdadc28304d27fef698cf4155235d07ecfaf5a2c5f8610a63ee809b0e0260251c33873dceebdda1ec3725d1376031e45cc".
"731a870b39edc97b549b96624c891984acf7a422584bc56f2104256f15da552d0a8376a546b6966153728ca1f38514df0d458375e99bc01fa498b07abb33803f".
"da07c4149e6e5773f9ec65ac3c87ca7c515f263de3cda2d53edbc20c47486ee33f9810c8226bbc9c52fcadb1f01fe28bf099b8afb9f1798e0b9815210c559187".
"c562b5e45350a5d0708c2fb96bad405ef4b8b535066ed02da198e4a3a4eaf075450c87f6d9840c8e00b8e316bcc7a5c6113fefbd72b0c7f6860fcecc8a3f33fb".
"a2999e4f3f3e3da5d7bfcf5d22a93f4d16ae6dd053685dfc7223628f92086735d09551bd29e8d0f537d06f33536fce8360d7443f583e9079685efce0347c1ffa".
"fedd0b7d1125f0dfc9bb21460079f286abbbeb549bb744aeea0b7a6bc66a272c8af945621b57b8380d40fa067c3060b9d44b79bd4333ec96d47632124a9aad0a".
"2df287eda9312f70f12f544fd7bdef9e6cc5e110effb8dbdebb821571f0fa95301db9da0bb60b77af6d5b7de00ca26039f1dda92f7a777c75d02fc340f1b81b5".
"e7c5efc6aaa6ffe3b77db348b7a5973a9465cb1e01841fa10f398318bfb73a4f8f53a4bded656f35db0ef00685826d8eac3aa0941623b3401ffdaba927bc91f4".
"808818548a60f653e9f340f79e40d666525923c4847ac3c0a9b36f3069620b0aea677ee7afa2c333987d9a5afade1b0e1e22ef7470228b07c9f482a6c343a37c".
"462a749c02d4cc86447cc16c3c68955afa80e63a3a41aaa1375c7ca0cffa0335e96e599e1b6841ae5693b5fa6ff437c3c1dca20075b7a58aafa81845af0aa8f6".
"30520d89a362d667447045c2b39f88f573f6b76b95ea4a98950ad797570b841975e9841306223dbefd21a4f092d69452c4539c664e27e110622ae7a7db5073d6".
"17eb023b36f28a13eeeebdbd964df63dcb18762950b6bd3eeead2a25b9bba48060ac8b82af3f41ecafbb7134140ca8cc687b92eded8bdabd9567e50950ed617a".
"a114d3db8648f9ab48a622456aec56fe79cfa6225fc7fd3fb0607f9dbc1bd861b316600fc10163fe8098ea685bc3fe06435f51cb1ce7ffebae67b3114fadf8c8".
"808a4044bb06638d05bc9a73c44c5b1eb7c83cdb4bde51ffa85413a97fbd534ddb17dc899fc4e2ced6ed81eeb117b4c77f9ecd03251367649a5649ec58567907".
"4fc8c2702dc42a58308f4023fb2cd30c79ecb9a952cde77dfcf92d8ef234811c327112abd568c49d4bf693f611d07e433fcd0a396530c6a279eb3ba567d780b7".
"271b6bfc7f1683a6b9159e143788662e8c5f73dd25ab623633efe781edd647b32003c9f3eaf236d968244e4561bc855848b839bfb93af2ea3e230a30089230c4".
"2e593ed3b9be53d677a7c9da744ee1961aaccac237f9e0bc1f886a92d5f335c6c0b0250ea76fbdcd85ae9cf6afe7ab25fd6b4753be6505b986757b003b94a089".
"d6a42b1fb24d2249ec917bb0ad50c8bd31265f82071a0816c3f8985edf0311205f83eaf8ff5587a3c7c24938a3f0cf9ff438b567d71407a51292e6d7e3f939e6".
"cdbecd49e913793f73cb964406934907ca4d48f44bec301bdf0110986757fcac6c2cca84eb7c5fad1662d1a833d24fa356771d6b772759a4837d9872d23ff1ab".
"219597aadc062f317d6cbc044bf65dc5ddda95ddc34d68584b7db991c8441a43e0511f71b88dda141f36b7cb326650c3244b989f1b992d2baa318e2a76dd1c34".
"a946c843255f65c6896eac3a6774ceff50b6f66b752672f5ce8dc84149ba6b227da844254d01bf470f6c987e8b5df2168414bcee11ad8c131d16e43addbdd493".
"595117f4f211c5d6460ee1be41e72b42c21252ce6dcd9838e53b0e1fd8d1864c2d3d219b82d42d0446865848431658732a78f0d9348f8044fa7f576d11562d25".
"d7b681f714c4b43532543d27069a21d1d152e646c56d75229bb198f87676108306e68fa49751f3b1d678bbf1ea38b2e0712d896882b5ea1494136f23a7e1d528".
"ca456c6c2a2cfc8cb6b6e7e6526aaa1da082653492b624936213569892706d8f9c6496b1193ec5a4294e3c1da14b25c24337cf9bb3490ea3f8a54e0a5b9f77af".
"fc70fe8dcb7687a9f45c7ae3ee8f2a94fa58e6c920cce1f447fd60526fa71b6f1048a3dcc7680e3b20ac66d78290bfc3878e72d4876e014036b0b80b6be4bf2e".
"a358125bea811b51af76a0077b3a615750a9ca3368d1d17e060a0d37bfd3b13c91412ca83298b06aea3048607f718c04667dcfc7faa4ac5a594be1c1551140ba".
"9c1ea7cebc074b1fbd338eef831fa3eb1f39088bcf1cf13bf706b1d287e12b165f4fb3e6c4586067c5e2f461c4cc86400b456428e8767c1b57a7bc3e64a8abe6".
"d253646f8796763b2a33de35c6f1667d06f30bb12c0fd0e28e4859ebdc2f96236af4a895d9a7d6fb90cbb60084db28a0c628faf7653c316ec69b5c5103aea495".
"792efd58ec42bc950f8608d5fa6834aab7bd2aaece33b3e16756f518a5410e8957dd534437e8c152451d86beb20124e8fb9e672d13fb7e98e153c124fdb2eaf7".
"f94a23efffeea25ec31f821e492d9de00a6d056c67e565f734f864d425035bb13620b7a1f44ec02ab7a6b1c4a38511b6902cfcf199d3918eb07da11d634add44".
"0860d123fa2b8003f87270777c6415e32f1b34dd6e1e22df3a78684e1169fce84b61cf461544f4e891fcd9d1f5a1e5fef148aeddbfcc922f5d7bfd3bd2480e8a".
"3318c75ce0afc24ca179fc0e832ab64368c174407bf2cd45a72cd5c9e7dd0b9def7500cec54d4d692938a1bb18289189d4b2445640d8abc9a0b70c3ffc8ba3c8".
"d483119a4f63851a57cf30f48c88616785a5ee00cb9221db45dd8dff118ca33bb4ae254937891f2c971edc8614fa3fc43e56f297a44a234fb1737f23d44a15f0".
"6a9e364fe1daa8e28bf72927526296202713f76dc8342e3843483b479ff793697b11a934bdc206905dd020e2f321cf8d65c245a8e7c4275f87301211800f0751".
"4e9cb59b88540f5441e6b09b4b73112d855ba0dffd4affd670c4f76ec11ac07a6cc2201ac65c83b3b3e4dc10d991ef4424cd001d34f0393dc262957df641469a".
"e00f74c527f8c99f50432c5ff4c4260ec6998b7ef2a0223290762126542d8aa89bfd241ac59e3a9a6c6f13afc9d69a771d124d16359525e4b374605b699e32bd".
"fb393d9397767bce32ab2d5557d05c33fa54183b0d5facc73a097441aa34abf7d6ac36fb35d6be7f19d0c26c7ad564c06f8a4f616ff4819c53e8b29e782b8791".
"c4039e5d049bd36819ae6d01a113eae6260e25150b935ee364011558dea97e1ee0e7f2938b7368ad9a5a86bae4f89a9ffbd06638566a785cb6ad3982b133ce6a".
"3edb13aa2c4ad4db7052ac646fcf336b375efb6a360d448862f2b711db3d8e657a706c14013664beae06b1a067fd078b0a8800c01dd610d583bee4fa4634e4f3".
"5251372b8144a7194ed60dc2539283ce909e7d65338a9050b09b66b647f30b6d595d7e03d9a77029afce140df7717f64949ae1362f94602dc2e70840e3117ab1".
"a26cc8e8ffd068ec225f0b75b2de63e3511f4485c87fb0087e4421675f3754bc4bc9c0a38db6392661e8a59802d83f887cf81aa99ed13a10b4b8a176144f76ce".
"3a192cc77b09e3f8a087db488f3d304d048623f46a031ba9251896cd08ff601dd0b933f5110b4cc9d943b5705b2435fa1c0adaad6c3aed88022f57cc3d71048f".
"9d5f420cfaf737b8a9f2434601b296b14384618fa9b76e6acbf1b55ad7130f582f36920a5aff71e15d120b11d6e0dd374554803538f3b12305512cf24322ed52".
"cd7ce5f409efd2f2752684bc326bf4548fa17169028c819ba342ee672682860a6de09752f509caad897484160895dc712b70bd05d588fe218fd85718b9b833ff".
"2c18e2566416ce1e52c3d7dc696cca1ad02b9b99e2953f92d8fe7ac0e4d75bd2ae2834b9ad8e87f179cdaf5e75609abdf1236787fe366347c32991f20c7faf41".
"b65da4ed5edc3cab1134a4ee0a3b565cab7c6dcd6f93feb528ddf0a1e992f6ad4814e51d338433dc5b52fddd8e780a312d12c80c4dbdaf8818b1c84883d8be41".
"186de5fdeeb9c7b7542a8429e53645a313cd8c9a53c3790b9fcf0143421da3bb586762790c91b0110f68b5fd111338560437d7d77457fb5587efb40a90ed1c02".
"838ba4e83b0c6adb175d94b6e14767a4f4a127e80f79be7741f4dc446c520176fd5b0412cc4d7a8f3d293e438d50e4e79e52bbc2c3bc6707d97b6289f1b39733".
"48c9351b66be55b2152bee9b76c42dc057d12134180488f45aee9491fe72f8634e3beeda8006869a829d2d58614150ab489dca7af268c09dde668cc20428ff88".
"366a3c0119446bdba29c39b0723fcd639393d397d138ab241c187beac647d8f73e5e42b3468e3958e0e73908c081ce0b6c894f0409f3bd321807a1633860a8e7".
"49cb4a10875a65b3f0a073f48f141747c88afe9039ef0795752dbd07ef51a2dadb40bb09bb9d4fcb328f68af28f8d76085fccaef4afe848a93c4cac43f55863a".
"21b540e6d408eb55fdfbd2a0c13fbae6fdf68e51423737f6966105d1ed57570bb521adb9576b06988d7d5a6445fe77d177076d47ca45b437a9780b376d49689e".
"6b0be983d90f46dbf935e14b53f3bf7ac7aec7fc1b92c14f161e59ae2620f7552206f22a365c91476943b8b51e920661efc19d040070407ba1cf011d3a0e072e".
"68d10e064619aa2184d7e848729b254af6b83db15fca2134d0d54efc761fff25c1169d608ed2434de8ae3cafb8c3af0b5b23a16183b5ead5dc5d175c955f4db5".
"454623d611244c462776118992ba03e8e20e6e1d9d6101d2286d7e040d5a56f22d6e3ae86bd6a0605c8b34d7a385fee5f3c9b6d0cf550f7aa67f338d8a014dfd".
"639cade855e8d25df73ea01bc5635bb5e032269b2a10f6b2baea7c4a88ede42caf91d7c9d3b2802608fdc361e23ee8cdcc1c954da86f929e9721130ef6d74e99".
"180f8c8c2263b41f538e105bc5f411f8dd1c2d3e0dc4540ff9cbdb9a6c44524ebcdfe37d9427a43dc24fd28c2fc25baef96490ae847b435ef4eea87db030829d".
"06b4c5d9271c8ffda114c336f5d82f9e6ca0d140112f364b1613cfe84c6e924629cba51a7d21f92ce26802bda0651340a8aad0c1ef439acc5552634304321cf6".
"02851751630d671a8cce7028f1cc6fdbce64f762c8ed522c2a81c2886986999a85d41a87d2ba5281dcbc2dbd728559470017e12fd70a97a771de499d2953c49b".
"0e60abac5ced203dd26bb75df922938723b1341bb07b0250d7af1bf91788994f8ed193221dd829e6665b114763e490fd8482955b097ac3b5b124bf92ae8ce902".
"1897b67db820cbfd646fe2c61e63baa972651a47bb1aae56f5e623a1167beff84166ea78cc9854b21a9478ebf3a1429226213c20a7a9ce8031eced508b937263".
"1357591069d5c482c0f6f99e4a6084f34fdab7b26399b4efcb0e5217e4e9115d0f6011bcfe55e0f05d3d8850febab0a6100bab8142a3913662a568f9d32367bf".
"5db46b6572cb76bd6a49d84bd567e1f834bbd705dd395c1609e9eba7fe8b9c59f1c4cb2561461204805c25a384140314e515f84050949529050279393884f8d0");
$game_key = "c6mw4it2kg7sz5o0813d9qyufenhj\x00";
$buffer_length = strlen($buffer);
$game_key_length = strlen($game_key);
// We want to encrpt the data
if ($encrypt)
{
for ($i=1; $i<$buffer_length; $i++)
{
$buffer[$i] = chr(ord($buffer[$i]) ^ ord($buffer[$i-1]));
}
for ($i=0; $i<$buffer_length; $i++)
{
$buffer[$i] = chr(ord($buffer[$i]) ^ ord($game_key[($i % 128) % $game_key_length]) ^ ord($master_key[$i % 128]) ^ ord($master_key[$i]));
}
}
else // We need to decrypt the data
{
for ($i=0; $i<$buffer_length; $i++)
{
$buffer[$i] = chr(ord($buffer[$i]) ^ ord($master_key[$i]) ^ ord($master_key[$i%128]) ^ ord($game_key[($i%128) % $game_key_length]));
}
for ($i=($buffer_length-1); $i>0; $i--)
{
$buffer[$i] = chr(ord($buffer[$i]) ^ ord($buffer[$i-1]));
}
}
return $buffer;
}
}

View File

@ -0,0 +1,28 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Alien Swarm Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Alienswarm extends GameQ_Protocols_Source
{
protected $name = "alienswarm";
protected $name_long = "Alien Swarm";
}

View File

@ -0,0 +1,28 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Age of Chivalry Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Aoc extends GameQ_Protocols_Source
{
protected $name = "aoc";
protected $name_long = "Age of Chivalry";
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Armed Assault Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Armedassault extends GameQ_Protocols_Gamespy2
{
protected $name = "armedassault";
protected $name_long = "Armed Assault";
protected $port = 2302;
}

View File

@ -0,0 +1,44 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Armed Assault 2 Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Armedassault2 extends GameQ_Protocols_Gamespy3
{
protected $name = "armedassault2";
protected $name_long = "Armed Assault 2";
protected $port = 2302;
protected function parsePlayerTeamInfoNew(GameQ_Buffer &$buf, GameQ_Result &$result)
{
// Read the buffer and replace the team_ sub-section under the players section becasue it is broke
$buf_fixed = preg_replace('/team_(.*)score_/m', 'score_', $buf->getBuffer());
// Replace the buffer with the "fixed" buffer
$buf = new GameQ_Buffer($buf_fixed);
unset($buf_fixed);
// Now we continue on with the parent
return parent::parsePlayerTeamInfo($buf, $result);
}
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Armed Assault 2: Operation Arrowhead Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Armedassault2oa extends GameQ_Protocols_Armedassault2
{
protected $name = "armedassault2oa";
protected $name_long = "Armed Assault 2: Operation Arrowhead";
protected $port = 2302;
}

View File

@ -0,0 +1,33 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Armed Assault 2 Protocol Class
*
* Special thanks to firefly2442 for linking working python script that
* supported both GSv2&3
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Armedassault3 extends GameQ_Protocols_Gamespy3
{
protected $name = "armedassault3";
protected $name_long = "Armed Assault 3";
protected $port = 2302;
}

View File

@ -0,0 +1,148 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* All-Seeing Eye Protocol Class
*
* This class is used as the basis for all game servers
* that use the All-Seeing Eye (ASE) protocol for querying
* server status.
*
* Most of the logic is taken from the original GameQ
* by Tom Buskens <t.buskens@deviation.nl>
*
* @author Marcel Bößendörfer <m.boessendoerfer@marbis.net>
* @author Austin Bischoff <austin@codebeard.com>
*/
abstract class GameQ_Protocols_ASE extends GameQ_Protocols
{
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @var array
*/
protected $packets = array(
self::PACKET_ALL => "s",
);
/**
* Methods to be run when processing the response(s)
*
* @var array
*/
protected $process_methods = array(
"process_all",
);
/**
* Default port for this server type
*
* @var int
*/
protected $port = 1; // Default port, used if not set when instanced
/**
* The protocol being used
*
* @var string
*/
protected $protocol = 'ase';
/**
* String name of this protocol class
*
* @var string
*/
protected $name = 'ase';
/**
* Longer string name of this protocol class
*
* @var string
*/
protected $name_long = "All-Seeing Eye";
/*
* Internal methods
*/
protected function process_all()
{
if(!$this->hasValidResponse(self::PACKET_ALL))
{
return array();
}
$data = $this->packets_response[self::PACKET_ALL][0];
$buf = new GameQ_Buffer($data);
$result = new GameQ_Result();
if ($buf->read(4) !== 'EYE1') {
throw new GameQException($data);
}
// Variables
$result->add('gamename', $buf->readPascalString(1, true));
$result->add('port', $buf->readPascalString(1, true));
$result->add('servername', $buf->readPascalString(1, true));
$result->add('gametype', $buf->readPascalString(1, true));
$result->add('map', $buf->readPascalString(1, true));
$result->add('version', $buf->readPascalString(1, true));
$result->add('password', $buf->readPascalString(1, true));
$result->add('num_players', $buf->readPascalString(1, true));
$result->add('max_players', $buf->readPascalString(1, true));
// Key / value pairs
while ($buf->getLength()) {
// If we have an empty key, we've reached the end
$key = $buf->readPascalString(1, true);
if (empty($key)) break;
// Otherwise, add the pair
$result->add(
$key,
$buf->readPascalString(1, true)
);
}
// Players
while ($buf->getLength()) {
// Get the flags
$flags = $buf->readInt8();
// Get data according to the flags
if ($flags & 1) {
$result->addPlayer('name', $buf->readPascalString(1, true));
}
if ($flags & 2) {
$result->addPlayer('team', $buf->readPascalString(1, true));
}
if ($flags & 4) {
$result->addPlayer('skin', $buf->readPascalString(1, true));
}
if ($flags & 8) {
$result->addPlayer('score', $buf->readPascalString(1, true));
}
if ($flags & 16) {
$result->addPlayer('ping', $buf->readPascalString(1, true));
}
if ($flags & 32) {
$result->addPlayer('time', $buf->readPascalString(1, true));
}
}
return $result->fetch();
}
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Aliens vs Preadtor Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Avp extends GameQ_Protocols_Gamespy
{
protected $name = "avp";
protected $name_long = "Aliens vs Preadtor";
protected $port = 27888;
}

View File

@ -0,0 +1,32 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Aliens vs Predator 2 Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Avp2 extends GameQ_Protocols_Gamespy
{
protected $name = "avp2";
protected $name_long = "Aliens vs Predator 2";
protected $state = self::STATE_TESTING;
protected $port = 27888;
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Battlefield 1942 Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Bf1942 extends GameQ_Protocols_Gamespy
{
protected $name = "bf1942";
protected $name_long = "Battlefield 1942";
protected $port = 23000;
}

View File

@ -0,0 +1,56 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Battlefield 2 Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Bf2 extends GameQ_Protocols_Gamespy3
{
protected $name = "bf2";
protected $name_long = "Battlefield 2";
protected $port = 29900;
/**
* Set the packet mode to multi, Gamespy v3 is by default a linear set of calls
*
* @var string
*/
protected $packet_mode = self::PACKET_MODE_MULTI;
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @var array
*/
protected $packets = array(
self::PACKET_ALL => "\xFE\xFD\x00\x10\x20\x30\x40\xFF\xFF\xFF\x01",
);
/**
* Methods to be run when processing the response(s)
*
* @var array
*/
protected $process_methods = array(
"process_all",
);
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Battlefield 2142 Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Bf2142 extends GameQ_Protocols_Gamespy3
{
protected $name = "bf2142";
protected $name_long = "Battlefield 2142";
protected $port = 29900;
}

View File

@ -0,0 +1,329 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Battlefield 3 Protocol Class
*
* Good place for doc status and info is http://www.fpsadmin.com/forum/showthread.php?t=24134
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Bf3 extends GameQ_Protocols
{
/**
* Normalization for this protocol class
*
* @var array
*/
protected $normalize = array(
// General
'general' => array(
'dedicated' => array('dedicated'),
'hostname' => array('hostname'),
'password' => array('password'),
'numplayers' => array('numplayers'),
'maxplayers' => array('maxplayers'),
'mapname' => array('map'),
'gametype' => array('gametype'),
'players' => array('players'),
'teams' => array('team'),
),
// Player
'player' => array(
'score' => array('score'),
),
// Team
'team' => array(
'score' => array('tickets'),
),
);
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @var array
*/
protected $packets = array(
self::PACKET_STATUS => "\x00\x00\x00\x00\x1b\x00\x00\x00\x01\x00\x00\x00\x0a\x00\x00\x00serverInfo\x00",
self::PACKET_VERSION => "\x00\x00\x00\x00\x18\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00version\x00",
self::PACKET_PLAYERS => "\x00\x00\x00\x00\x24\x00\x00\x00\x02\x00\x00\x00\x0b\x00\x00\x00listPlayers\x00\x03\x00\x00\x00\x61ll\x00",
);
/**
* Set the transport to use TCP
*
* @var string
*/
protected $transport = self::TRANSPORT_TCP;
/**
* Methods to be run when processing the response(s)
*
* @var array
*/
protected $process_methods = array(
"process_status",
"process_version",
"process_players",
);
/**
* Default port for this server type
*
* @var int
*/
protected $port = 25200; // Default port, used if not set when instanced
/**
* The protocol being used
*
* @var string
*/
protected $protocol = 'bf3';
/**
* String name of this protocol class
*
* @var string
*/
protected $name = 'bf3';
/**
* Longer string name of this protocol class
*
* @var string
*/
protected $name_long = "Battlefield 3";
/*
* Internal methods
*/
protected function preProcess_status($packets=array())
{
// Implode and return
return implode('', $packets);
}
protected function process_status()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_STATUS))
{
return array();
}
// Make buffer for data
$buf = new GameQ_Buffer($this->preProcess_status($this->packets_response[self::PACKET_STATUS]));
$buf->skip(8); /* skip header */
// Decode the words into an array so we can use this data
$words = $this->decodeWords($buf);
// Make sure we got OK
if (!isset ($words[0]) || $words[0] != 'OK')
{
throw new GameQ_ProtocolsException('Packet Response was not OK! Buffer:'.$buf->getBuffer());
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Server is always dedicated
$result->add('dedicated', TRUE);
// No mods, as of yet
$result->add('mod', FALSE);
// These are the same no matter what mode the server is in
$result->add('hostname', $words[1]);
$result->add('numplayers', $words[2]);
$result->add('maxplayers', $words[3]);
$result->add('gametype', $words[4]);
$result->add('map', $words[5]);
$result->add('roundsplayed', $words[6]);
$result->add('roundstotal', $words[7]);
// Figure out the number of teams
$num_teams = intval($words[8]);
// Set the current index
$index_current = 9;
// Loop for the number of teams found, increment along the way
for($id=1; $id<=$num_teams; $id++)
{
$result->addSub('teams', 'tickets', $words[$index_current]);
$result->addSub('teams', 'id', $id);
// Increment
$index_current++;
}
// Get and set the rest of the data points.
$result->add('targetscore', $words[$index_current]);
$result->add('online', TRUE); // Forced TRUE, it seems $words[$index_current + 1] is always empty
$result->add('ranked', $words[$index_current + 2] === 'true');
$result->add('punkbuster', $words[$index_current + 3] === 'true');
$result->add('password', $words[$index_current + 4] === 'true');
$result->add('uptime', $words[$index_current + 5]);
$result->add('roundtime', $words[$index_current + 6]);
// Added in R9
$result->add('ip_port', $words[$index_current + 7]);
$result->add('punkbuster_version', $words[$index_current + 8]);
$result->add('join_queue', $words[$index_current + 9] === 'true');
$result->add('region', $words[$index_current + 10]);
$result->add('pingsite', $words[$index_current + 11]);
$result->add('country', $words[$index_current + 12]);
// Added in R29, No docs as of yet
$result->add('quickmatch', $words[$index_current + 13] === 'true'); // Guessed from research
unset($buf, $words);
return $result->fetch();
}
protected function preProcess_version($packets=array())
{
// Implode and return
return implode('', $packets);
}
protected function process_version()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_VERSION))
{
return array();
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Make buffer for data
$buf = new GameQ_Buffer($this->preProcess_version($this->packets_response[self::PACKET_VERSION]));
$buf->skip(8); /* skip header */
$words = $this->decodeWords($buf);
// Not too important if version is missing
if (!isset ($words[0]) || $words[0] != 'OK')
{
return array();
}
$result->add('version', $words[2]);
unset($buf, $words);
return $result->fetch();
}
protected function preProcess_players($packets=array())
{
// Implode and return
return implode('', $packets);
}
protected function process_players()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_PLAYERS))
{
return array();
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Make buffer for data
$buf = new GameQ_Buffer($this->preProcess_players($this->packets_response[self::PACKET_PLAYERS]));
$buf->skip(8); /* skip header */
$words = $this->decodeWords($buf);
// Not too important if players are missing.
if (!isset ($words[0]) || $words[0] != 'OK')
{
return array();
}
// Count the number of words and figure out the highest index.
$words_total = count($words)-1;
// The number of player info points
$num_tags = $words[1];
// Pull out the tags, they start at index=3, length of num_tags
$tags = array_slice($words, 2, $num_tags);
// Just incase this changed between calls.
$result->add('numplayers', $words[9]);
// Loop until we run out of positions
for($pos=(3+$num_tags);$pos<=$words_total;$pos+=$num_tags)
{
// Pull out this player
$player = array_slice($words, $pos, $num_tags);
// Loop the tags and add the proper value for the tag.
foreach($tags AS $tag_index => $tag)
{
$result->addPlayer($tag, $player[$tag_index]);
}
// No pings in this game
$result->addPlayer('ping', FALSE);
}
// @todo: Add some team definition stuff
unset($buf, $tags, $words, $player);
return $result->fetch();
}
/**
* Decode words from the response
*
* @param GameQ_Buffer $buf
*/
protected function decodeWords(GameQ_Buffer &$buf)
{
$result = array();
$num_words = $buf->readInt32();
for ($i = 0; $i < $num_words; $i++)
{
$len = $buf->readInt32();
$result[] = $buf->read($len);
$buf->read(1); /* 0x00 string ending */
}
return $result;
}
}

View File

@ -0,0 +1,249 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Battlefield Bad Company 2 Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Bfbc2 extends GameQ_Protocols
{
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @var array
*/
protected $packets = array(
self::PACKET_STATUS => "\x00\x00\x00\x00\x1b\x00\x00\x00\x01\x00\x00\x00\x0a\x00\x00\x00serverInfo\x00",
self::PACKET_VERSION => "\x00\x00\x00\x00\x18\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00version\x00",
self::PACKET_PLAYERS => "\x00\x00\x00\x00\x24\x00\x00\x00\x02\x00\x00\x00\x0b\x00\x00\x00listPlayers\x00\x03\x00\x00\x00\x61ll\x00",
);
/**
* Set the transport to use TCP
*
* @var string
*/
protected $transport = self::TRANSPORT_TCP;
/**
* Methods to be run when processing the response(s)
*
* @var array
*/
protected $process_methods = array(
"process_status",
"process_version",
"process_players",
);
/**
* Default port for this server type
*
* @var int
*/
protected $port = 48888; // Default port, used if not set when instanced
/**
* The protocol being used
*
* @var string
*/
protected $protocol = 'bfbc2';
/**
* String name of this protocol class
*
* @var string
*/
protected $name = 'bfbc2';
/**
* Longer string name of this protocol class
*
* @var string
*/
protected $name_long = "Battlefield Bad Company 2";
/*
* Internal methods
*/
protected function preProcess_status($packets=array())
{
// Implode and return
return implode('', $packets);
}
protected function process_status()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_STATUS))
{
return array();
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Make buffer for data
$buf = new GameQ_Buffer($this->preProcess_status($this->packets_response[self::PACKET_STATUS]));
$buf->skip(8); /* skip header */
$words = $this->decodeWords($buf);
if (!isset ($words[0]) || $words[0] != 'OK')
{
throw new GameQ_ProtocolsException('Packet Response was not OK! Buffer:'.$buf->getBuffer());
}
$result->add('hostname', $words[1]);
$result->add('numplayers', $words[2]);
$result->add('maxplayers', $words[3]);
$result->add('gametype', $words[4]);
$result->add('map', $words[5]);
// @todo: Add some team definition stuff
unset($buf);
return $result->fetch();
}
protected function preProcess_version($packets=array())
{
// Implode and return
return implode('', $packets);
}
protected function process_version()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_VERSION))
{
return array();
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Make buffer for data
$buf = new GameQ_Buffer($this->preProcess_version($this->packets_response[self::PACKET_VERSION]));
$buf->skip(8); /* skip header */
$words = $this->decodeWords($buf);
// Not too important if version is missing
if (!isset ($words[0]) || $words[0] != 'OK')
{
return array();
}
$result->add('version', $words[2]);
unset($buf);
return $result->fetch();
}
protected function preProcess_players($packets=array())
{
// Implode and return
return implode('', $packets);
}
protected function process_players()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_PLAYERS))
{
return array();
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Make buffer for data
$buf = new GameQ_Buffer($this->preProcess_players($this->packets_response[self::PACKET_PLAYERS]));
$buf->skip(8); /* skip header */
$words = $this->decodeWords($buf);
// Not too important if players are missing.
if (!isset ($words[0]) || $words[0] != 'OK')
{
return array();
}
// The number of player info points
$num_tags = $words[1];
$position = 2;
$tags = array();
for (; $position < $num_tags + 2 ; $position++)
{
$tags[] = $words[$position];
}
$num_players = $words[$position];
$position++;
$start_position = $position;
for (; $position < $num_players * $num_tags + $start_position;
$position += $num_tags)
{
for ($a = $position, $b = 0; $a < $position + $num_tags;
$a++, $b++)
{
$result->addPlayer($tags[$b], $words[$a]);
}
}
// @todo: Add some team definition stuff
unset($buf);
return $result->fetch();
}
/**
* Decode words from the response
*
* @param GameQ_Buffer $buf
*/
protected function decodeWords(GameQ_Buffer &$buf)
{
$result = array();
$num_words = $buf->readInt32();
for ($i = 0; $i < $num_words; $i++)
{
$len = $buf->readInt32();
$result[] = $buf->read($len);
$buf->read(1); /* 0x00 string ending */
}
return $result;
}
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Battlefield Vietnam Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Bfv extends GameQ_Protocols_Gamespy2
{
protected $name = "bfv";
protected $name_long = "Battlefield Vietnam";
protected $port = 23000;
}

View File

@ -0,0 +1,28 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Brink Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Brink extends GameQ_Protocols_Source
{
protected $name = "brink";
protected $name_long = "Brink";
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Call of Duty Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Cod extends GameQ_Protocols_Quake3
{
protected $name = "cod";
protected $name_long = "Call of Duty";
protected $port = 28960;
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Call of Duty 2 Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Cod2 extends GameQ_Protocols_Quake3
{
protected $name = "cod2";
protected $name_long = "Call of Duty 2";
protected $port = 28960;
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Call of Duty 4 Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Cod4 extends GameQ_Protocols_Quake3
{
protected $name = "cod4";
protected $name_long = "Call of Duty 4";
protected $port = 28960;
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Call of Duty: Modern Warfare 3 Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Codmw3 extends GameQ_Protocols_Source
{
protected $name = "codmw3";
protected $name_long = "Call of Duty: Modern Warfare 3";
protected $port = 27015;
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Call of Duty: United Offensive Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Coduo extends GameQ_Protocols_Quake3
{
protected $name = "coduo";
protected $name_long = "Call of Duty: United Offensive";
protected $port = 28960;
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Call of Duty: World at War Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Codwaw extends GameQ_Protocols_Quake3
{
protected $name = "codwaw";
protected $name_long = "Call of Duty: World at War";
protected $port = 28960;
}

View File

@ -0,0 +1,637 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
/**
* Handles the core functionality for the protocols
*
* @author Austin Bischoff <austin@codebeard.com>
*/
abstract class GameQ_Protocols_Core
{
/*
* Constants for class states
*/
const STATE_TESTING = 1;
const STATE_BETA = 2;
const STATE_STABLE = 3;
const STATE_DEPRECATED = 4;
/*
* Constants for packet keys
*/
const PACKET_ALL = 'all'; // Some protocols allow all data to be sent back in one call.
const PACKET_BASIC = 'basic';
const PACKET_CHALLENGE = 'challenge';
const PACKET_CHANNELS = 'channels'; // Voice servers
const PACKET_DETAILS = 'details';
const PACKET_INFO = 'info';
const PACKET_PLAYERS = 'players';
const PACKET_STATUS = 'status';
const PACKET_RULES = 'rules';
const PACKET_VERSION = 'version';
/*
* Transport constants
*/
const TRANSPORT_UDP = 'udp';
const TRANSPORT_TCP = 'tcp';
/**
* Can only send one packet at a time, slower
*
* @var string
*/
const PACKET_MODE_LINEAR = 'linear';
/**
* Can send multiple packets at once and get responses, after challenge request (if required)
*
* @var string
*/
const PACKET_MODE_MULTI = 'multi';
/**
* Current version of this class
*
* @var string
*/
protected $version = '2.0';
/**
* Short name of the protocol
*
* @var string
*/
protected $name = 'unnamed';
/**
* The longer, fancier name for the protocol
*
* @var string
*/
protected $name_long = 'unnamed';
/**
* IP address of the server we are querying.
*
* @var string
*/
protected $ip = '127.0.0.1';
/**
* Port of the server we are querying.
*
* @var mixed FALSE|int
*/
protected $port = NULL;
/**
* The trasport method to use to actually send the data
* Default is UDP
*
* @var string UDP|TCP
*/
protected $transport = self::TRANSPORT_UDP;
/**
* The protocol type used when querying the server
*
* @var string
*/
protected $protocol = 'unknown';
/**
* Packets Mode is multi by default since most games support it
*
* @var string
*/
protected $packet_mode = self::PACKET_MODE_MULTI;
/**
* Holds the valid packet types this protocol has available.
*
* @var array
*/
protected $packets = array();
/**
* Holds the list of methods to run when parsing the packet response(s) data. These
* methods should provide all the return information.
*
* @var array()
*/
protected $process_methods = array();
/**
* The packet responses received
*
* @var array
*/
protected $packets_response = array();
/**
* Holds the instance of the result class
*
* @var GameQ_Result
*/
protected $result = NULL;
/**
* Options for this protocol
*
* @var array
*/
protected $options = array();
/**
* Holds the challenge response, if there is a challenge needed.
*
* @var array
*/
protected $challenge_response = NULL;
/**
* Holds the challenge buffer.
*
* @var GameQ_Buffer
*/
protected $challenge_buffer = NULL;
/**
* Holds the result of the challenge, if any
* Will hold the error here
*
* @var mixed
*/
protected $challenge_result = TRUE;
/**
* Define the state of this class
*
* @var int
*/
protected $state = self::STATE_STABLE;
/**
* Holds and changes we want to make to the normailze filter
*
* @var array
*/
protected $normalize = FALSE;
/**
* Create the instance.
*
* @param string $ip
* @param mixed $port false|int
* @param array $options
*/
public function __construct($ip = FALSE, $port = FALSE, $options = array())
{
$this->ip($ip);
// We have a specific port set so let's set it.
if($port !== FALSE)
{
$this->port($port);
}
// We have passed options so let's set them
if(!empty($options))
{
$this->options($options);
}
}
/**
* String name of this class
*/
public function __toString()
{
return $this->name;
}
/**
* Get an option's value
*
* @param string $option
* @return mixed
*/
public function __get($option)
{
return isset($this->options[$option]) ? $this->options[$option] : NULL;
}
/**
* Set an option's value
*
* @param string $option
* @param mixed $value
* @return boolean
*/
public function __set($option, $value)
{
$this->options[$option] = $value;
return TRUE;
}
/**
* Short (callable) name of this class
*
* @return string
*/
public function name()
{
return $this->name;
}
/**
* Long name of this class
*/
public function name_long()
{
return $this->name_long;
}
/**
* Return the status of this Protocol Class
*/
public function state()
{
return $this->state;
}
/**
* Return the packet mode for this protocol
*/
public function packet_mode()
{
return $this->packet_mode;
}
/**
* Return the protocol property
*
*/
public function protocol()
{
return $this->protocol;
}
/**
* Get/set the ip address of the server
*
* @param string $ip
*/
public function ip($ip = FALSE)
{
// Act as setter
if($ip !== FALSE)
{
$this->ip = $ip;
}
return $this->ip;
}
/**
* Get/set the port of the server
*
* @param int $port
*/
public function port($port = FALSE)
{
// Act as setter
if($port !== FALSE)
{
$this->port = $port;
}
return $this->port;
}
/**
* Get/set the transport type for this protocol
*
* @param string $type
*/
public function transport($type = FALSE)
{
// Act as setter
if($type !== FALSE)
{
$this->transport = $type;
}
return $this->transport;
}
/**
* Set the options for the protocol call
*
* @param array $options
*/
public function options($options = Array())
{
// Act as setter
if(!empty($options))
{
$this->options = $options;
}
return $this->options;
}
/**
* Determine whether or not this protocol has some kind of challenge
*/
public function hasChallenge()
{
return (isset($this->packets[self::PACKET_CHALLENGE]) && !empty($this->packets[self::PACKET_CHALLENGE]));
}
/**
* See if the challenge was ok
*/
public function challengeOK()
{
return ($this->challenge_result === TRUE);
}
/**
* Get/set the challenge response
*
* @param array $response
*/
public function challengeResponse($response = Array())
{
// Act as setter
if(!empty($response))
{
$this->challenge_response = $response;
}
return $this->challenge_response;
}
/**
* Get/set the challenge result
*
* @param string $result
*/
public function challengeResult($result = FALSE)
{
// Act as setter
if(!empty($result))
{
$this->challenge_result = $result;
}
return $this->challenge_result;
}
/**
* Get/set the challenge buffer
*
* @param GameQ_Buffer $buffer
*/
public function challengeBuffer($buffer = NULL)
{
// Act as setter
if(!empty($buffer))
{
$this->challenge_buffer = $buffer;
}
return $this->challenge_buffer;
}
/**
* Verify the challenge response and parse it
*/
public function challengeVerifyAndParse()
{
// Check to make sure the response exists
if(!isset($this->challenge_response[0]))
{
// Set error and skip
$this->challenge_result = 'Challenge Response Empty';
return FALSE;
}
// Challenge is good to go
$this->challenge_result = TRUE;
// Now let's create a new buffer with this response
$this->challenge_buffer = new GameQ_Buffer($this->challenge_response[0]);
// Now parse the challenge and apply it
return $this->parseChallengeAndApply();
}
/**
* Get/set the packet response
*
* @param string $packet_type
* @param array $response
*/
public function packetResponse($packet_type, $response = Array())
{
// Act as setter
if(!empty($response))
{
$this->packets_response[$packet_type] = $response;
}
return $this->packets_response[$packet_type];
}
/**
* Return specific packet(s)
*
* @param mixed $type array|string
*/
public function getPacket($type = array())
{
// We want an array of packets back
if(is_array($type) && !empty($type))
{
$packets = array();
// Loop the packets
foreach($this->packets AS $packet_type => $packet_data)
{
// We want this packet
if(in_array($packet_type, $type))
{
$packets[$packet_type] = $packet_data;
}
}
return $packets;
}
elseif($type == '!challenge')
{
$packets = array();
// Loop the packets
foreach($this->packets AS $packet_type => $packet_data)
{
// Dont want challenge packets
if($packet_type == self::PACKET_CHALLENGE)
{
continue;
}
$packets[$packet_type] = $packet_data;
}
return $packets;
}
elseif(is_string($type))
{
return $this->packets[$type];
}
// Return all the packets
return $this->packets;
}
/* Begin working methods */
/**
* Process the response and return the raw data as an array.
*
* @throws GameQException
*/
public function processResponse()
{
// Init the array
$results = array();
// Let's loop all the requred methods to get all the data we want/need.
foreach ($this->process_methods AS $method)
{
// Lets make sure the data method defined exists.
if(!method_exists($this, $method))
{
// We should never get here in a production environment
throw new GameQException('Unable to load method '.__CLASS__.'::'.$method);
return FALSE;
}
// Setup a catch for protocol level errors
try
{
// Call the proper process method. All methods should return an array of data.
// Preprocessing should be handled by these methods internally as well.
// Merge in the results when done.
$results = array_merge($results, call_user_func_array(array($this, $method), array()));
}
catch (GameQ_ProtocolsException $e)
{
// Check to see if we are in debug, if so bubble up the exception
if($this->debug)
{
throw new GameQException($e->getMessage(), $e->getCode(), $e);
return FALSE;
}
// We ignore this and continue
continue;
}
}
// Now add some default stuff
$results['gq_online'] = (count($results) > 0);
$results['gq_address'] = $this->ip;
$results['gq_port'] = $this->port;
$results['gq_protocol'] = $this->protocol;
$results['gq_type'] = (string) $this;
$results['gq_transport'] = $this->transport;
// Return the raw results
return $results;
}
/**
* This method is called before the actual query packets are sent to the server. This allows
* the class to modify any changes before being sent.
*
* @return boolean
*/
public function beforeSend()
{
return TRUE;
}
/**
* Get the normalize property
*/
public function getNormalize()
{
return $this->normalize;
}
/**
* Apply the challenge string to all the packets that need it.
*
* @param string $challenge_string
*/
protected function challengeApply($challenge_string)
{
// Let's loop thru all the packets and append the challenge where it is needed
foreach($this->packets AS $packet_type => $packet)
{
$this->packets[$packet_type] = sprintf($packet, $challenge_string);
}
return TRUE;
}
/**
* Parse the challenge buffer and get the proper challenge string out
*/
protected function parseChallengeAndApply()
{
return TRUE;
}
/**
* Determine whether or not the response is valid for a specific packet type
*
* @param string $packet_type
*/
protected function hasValidResponse($packet_type)
{
// Check for valid packet. All packet responses should have atleast 1 array key (0).
if(isset($this->packets_response[$packet_type][0])
&& !empty($this->packets_response[$packet_type][0])
)
{
return TRUE;
}
return FALSE;
}
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Crysis Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Crysis extends GameQ_Protocols_Gamespy3
{
protected $name = "crysis";
protected $name_long = "Crysis";
protected $port = 64087;
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Crysis 2 Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Crysis2 extends GameQ_Protocols_Gamespy3
{
protected $name = "crysis2";
protected $name_long = "Crysis 2";
protected $port = 64000;
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Crysis Warhead Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Crysiswarhead extends GameQ_Protocols_Gamespy3
{
protected $name = "crysiswarhead";
protected $name_long = "Crysis Warhead";
protected $port = 64100;
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Crysis Wars Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Crysiswars extends GameQ_Protocols_Gamespy3
{
protected $name = "crysiswars";
protected $name_long = "Crysis Wars";
protected $port = 64100;
}

View File

@ -0,0 +1,49 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Counter-Strike 1.6 Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Cs16 extends GameQ_Protocols_Source
{
protected $name = "cs16";
protected $name_long = "Counter-Strike 1.6";
/**
* We have to overload this function to cheat the rules processing because of some wierdness, old ass game!
*
* @see GameQ_Protocols_Source::preProcess_rules()
*/
protected function preProcess_rules($packets)
{
$engine_orig = $this->source_engine;
// Override the engine type for rules, not sure why its like that
$this->source_engine = self::GOLDSOURCE_ENGINE;
// Now process the rules
$ret = parent::preProcess_rules($packets);
// Reset the engine type
$this->source_engine = $engine_orig;
return $ret;
}
}

View File

@ -0,0 +1,49 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Counter-Strike: Condition Zero Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Cscz extends GameQ_Protocols_Source
{
protected $name = "cscz";
protected $name_long = "Counter-Strike: Condition Zero";
/**
* We have to overload this function to cheat the rules processing because of some wierdness, old ass game!
*
* @see GameQ_Protocols_Source::preProcess_rules()
*/
protected function preProcess_rules($packets)
{
$engine_orig = $this->source_engine;
// Override the engine type for rules, not sure why its like that
$this->source_engine = self::GOLDSOURCE_ENGINE;
// Now process the rules
$ret = parent::preProcess_rules($packets);
// Reset the engine type
$this->source_engine = $engine_orig;
return $ret;
}
}

View File

@ -0,0 +1,28 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Counter-Strike: Global Offensive Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Csgo extends GameQ_Protocols_Source
{
protected $name = "csgo";
protected $name_long = "Counter-Strike: Global Offensive";
}

View File

@ -0,0 +1,28 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Counter-Strike: Source Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Css extends GameQ_Protocols_Source
{
protected $name = "css";
protected $name_long = "Counter-Strike: Source";
}

View File

@ -0,0 +1,175 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Cube 2: Sauerbraten Protocol Class
*
* References:
* https://qstat.svn.sourceforge.net/svnroot/qstat/trunk/qstat2/cube2.c
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Cube2 extends GameQ_Protocols
{
protected $state = self::STATE_BETA;
protected $normalize = array(
// General
'general' => array(
'hostname' => array('servername'),
'numplayers' => array('num_players'),
'maxplayers' => array('max_players'),
'mapname' => array('map'),
'gametype' => array('gametype'),
),
);
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @var array
*/
protected $packets = array(
self::PACKET_STATUS => "server",
);
/**
* Methods to be run when processing the response(s)
*
* @var array
*/
protected $process_methods = array(
"process_status",
);
/**
* Default port for this server type
*
* @var int
*/
protected $port = 28802; // Default port, used if not set when instanced
/**
* The query protocol used to make the call
*
* @var string
*/
protected $protocol = 'cube2';
/**
* String name of this protocol class
*
* @var string
*/
protected $name = 'cube2';
/**
* Longer string name of this protocol class
*
* @var string
*/
protected $name_long = "Cube 2: Sauerbraten";
/**
* Pre-process the server status data that was returned.
*
* @param array $packets
*/
protected function preProcess_status($packets)
{
// Process the packets
return implode('', $packets);
}
/**
* Handles processing the status data into a usable format
*
* @throws GameQ_ProtocolsException
*/
protected function process_status()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_STATUS))
{
return array();
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Let's preprocess the rules
$data = $this->preProcess_status($this->packets_response[self::PACKET_STATUS]);
// Create a new buffer
$buf = new GameQ_Buffer($data);
// Check the header, should be the same response as the packet we sent
if($buf->read(6) != $this->packets[self::PACKET_STATUS])
{
throw new GameQ_ProtocolsException("Data for ".__METHOD__." does not have the proper header type (should be {$this->packets[self::PACKET_STATUS]}).");
return array();
}
// NOTE: the following items were figured out using some source and trial and error
$result->add('num_players', $this->readInt($buf));
$result->add('version', $this->readInt($buf));
$result->add('protocol', $this->readInt($buf));
$result->add('mode', $this->readInt($buf));
$result->add('time_remaining', $this->readInt($buf));
$result->add('max_players', $this->readInt($buf));
$result->add('mastermode', $this->readInt($buf));
// @todo: Sometimes there is an extra char here before the map string. Not sure what causes it or how
// to even check for its existance.
$result->add('map', $buf->readString());
$result->add('servername', $buf->readString());
unset($buf, $data);
return $result->fetch();
}
/**
* Function to check for varying int values in the responses. Makes life a little easier
*
* @param GameQ_Buffer $buf
* @return number
*/
protected function readInt(GameQ_Buffer &$buf)
{
// Look ahead and see if 32-bit int
if($buf->lookAhead(1) == "\x81")
{
$buf->skip(1);
return $buf->readInt32();
}
// Look ahead and see if 16-bit int
elseif($buf->lookAhead(1) == "\x80")
{
$buf->skip(1);
return $buf->readInt16();
}
else // 8-bit
{
return $buf->readInt8();
}
}
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* DayZ Mod Protocol Class
*
* @author Marcel Bößendörfer <m.boessendoerfer@marbis.net>
*/
class GameQ_Protocols_Dayz extends GameQ_Protocols_Armedassault2
{
protected $name = "dayz";
protected $name_long = "DayZ Mod";
protected $port = 2302;
}

View File

@ -0,0 +1,28 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Day of Defeat Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Dod extends GameQ_Protocols_Source
{
protected $name = "dod";
protected $name_long = "Day of Defeat";
}

View File

@ -0,0 +1,28 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Day of Defeat: Source Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Dods extends GameQ_Protocols_Source
{
protected $name = "dods";
protected $name_long = "Day of Defeat: Source";
}

View File

@ -0,0 +1,163 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Doom3 Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Doom3 extends GameQ_Protocols
{
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @var array
*/
protected $packets = array(
self::PACKET_ALL => "\xFF\xFFgetInfo\x00PiNGPoNG\x00",
);
/**
* Methods to be run when processing the response(s)
*
* @var array
*/
protected $process_methods = array(
"process_all",
);
/**
* Default port for this server type
*
* @var int
*/
protected $port = 27666; // Default port, used if not set when instanced
/**
* The protocol being used
*
* @var string
*/
protected $protocol = 'doom3';
/**
* String name of this protocol class
*
* @var string
*/
protected $name = 'doom3';
/**
* Longer string name of this protocol class
*
* @var string
*/
protected $name_long = "Doom 3";
/*
* Internal methods
*/
protected function preProcess_all($packets=array())
{
// Implode and return
return implode('', $packets);
}
protected function process_all()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_ALL))
{
return array();
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Parse the response
$data = $this->preProcess_all($this->packets_response[self::PACKET_ALL]);
// Create a new buffer
$buf = new GameQ_Buffer($data);
// Header
if ($buf->readInt16() !== 65535 or $buf->readString() !== 'infoResponse')
{
throw new GameQ_ProtocolsException('Header for response does not match. Buffer:'.$this->packets_response[self::PACKET_ALL]);
return array();
}
$result->add('version', $buf->readInt8() . '.' . $buf->readInt8());
// Var / value pairs, delimited by an empty pair
while ($buf->getLength())
{
$var = $buf->readString();
$val = $buf->readString();
// Something is empty so we are done
if (empty($var) && empty($val))
{
break;
}
$result->add($var, $val);
}
// Now lets parse the players
$this->parsePlayers($buf, $result);
unset($buf, $data);
// Return the result
return $result->fetch();
}
/**
* Parse the players. Set as its own method so it can be overloaded.
*
* @param GameQ_Buffer $buf
* @param GameQ_Result $result
*/
protected function parsePlayers(GameQ_Buffer &$buf, GameQ_Result &$result)
{
// There is no way to see the number of players so we have to increment
// a variable and do it that way.
$players = 0;
// Loop thru the buffer until we run out of data
while (($id = $buf->readInt8()) != 32)
{
$result->addPlayer('id', $id);
$result->addPlayer('ping', $buf->readInt16());
$result->addPlayer('rate', $buf->readInt32());
$result->addPlayer('name', $buf->readString());
$players++;
}
// Add the number of players to the result
$result->add('numplayers', $players);
return TRUE;
}
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Wolfenstein Enemy Territory Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Et extends GameQ_Protocols_Quake3
{
protected $name = "et";
protected $name_long = "Wolfenstein Enemy Territory";
protected $port = 27960;
}

View File

@ -0,0 +1,225 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Enemy Territory: Quake Wars Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Etqw extends GameQ_Protocols
{
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @var array
*/
protected $packets = array(
self::PACKET_STATUS => "\xFF\xFFgetInfoEx\x00\x00\x00\x00",
//self::PACKET_STATUS => "\xFF\xFFgetInfo\x00\x00\x00\x00\x00",
);
/**
* Methods to be run when processing the response(s)
*
* @var array
*/
protected $process_methods = array(
"process_status",
);
/**
* Default port for this server type
*
* @var int
*/
protected $port = 27733; // Default port, used if not set when instanced
/**
* The protocol being used
*
* @var string
*/
protected $protocol = 'etqw';
/**
* String name of this protocol class
*
* @var string
*/
protected $name = 'etqw';
/**
* Longer string name of this protocol class
*
* @var string
*/
protected $name_long = "Enemy Territory: Quake Wars";
/*
* Internal methods
*/
protected function preProcess_status($packets)
{
// Should only be one packet
if (count($packets) > 1)
{
throw new GameQ_ProtocolsException('Enemy Territor: Quake Wars status has more than 1 packet');
}
// Make buffer so we can check this out
$buf = new GameQ_Buffer($packets[0]);
// Grab the header
$header = $buf->readString();
// Now lets verify the header
if(!strstr($header, 'infoExResponse'))
{
throw new GameQ_ProtocolsException('Unable to match Enemy Territor: Quake Wars response header. Header: '. $header);
return FALSE;
}
// Return the data with the header stripped, ready to go.
return $buf->getBuffer();
}
/**
* Process the server status
*
* @throws GameQ_ProtocolsException
*/
protected function process_status()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_STATUS))
{
return array();
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Lets pre process and make sure these things are in the proper order by id
$data = $this->preProcess_status($this->packets_response[self::PACKET_STATUS]);
// Make buffer
$buf = new GameQ_Buffer($data);
// Now burn the challenge, version and size
$buf->skip(16);
// Key / value pairs
while ($buf->getLength())
{
$var = str_replace('si_', '', $buf->readString());
$val = $buf->readString();
if (empty($var) && empty($val))
{
break;
}
// Add the server prop
$result->add($var, $val);
}
// Now let's do the basic player info
$this->parsePlayers($buf, $result);
// Now grab the rest of the server info
$result->add('osmask', $buf->readInt32());
$result->add('ranked', $buf->readInt8());
$result->add('timeleft', $buf->readInt32());
$result->add('gamestate', $buf->readInt8());
$result->add('servertype', $buf->readInt8());
// 0: regular server
if ($result->get('servertype') == 0)
{
$result->add('interested_clients', $buf->readInt8());
}
// 1: tv server
else
{
$result->add('connected_clients', $buf->readInt32());
$result->add('max_clients', $buf->readInt32());
}
// Now let's parse the extended player info
$this->parsePlayersExtra($buf, $result);
// Free some memory
unset($sections, $buf, $data);
// Return the result
return $result->fetch();
}
/**
* Parse the players and add them to the return.
*
* @param GameQ_Buffer $buf
* @param GameQ_Result $result
*/
protected function parsePlayers(GameQ_Buffer &$buf, GameQ_Result &$result)
{
$players = 0;
while (($id = $buf->readInt8()) != 32)
{
$result->addPlayer('id', $id);
$result->addPlayer('ping', $buf->readInt16());
$result->addPlayer('name', $buf->readString());
$result->addPlayer('clantag_pos', $buf->readInt8());
$result->addPlayer('clantag', $buf->readString());
$result->addPlayer('bot', $buf->readInt8());
$players++;
}
// Let's add in the current players as a result
$result->add('numplayers', $players);
// Free some memory
unset($id);
}
/**
* Parse the players extra info and add them to the return.
*
* @param GameQ_Buffer $buf
* @param GameQ_Result $result
*/
protected function parsePlayersExtra(GameQ_Buffer &$buf, GameQ_Result &$result)
{
while (($id = $buf->readInt8()) != 32)
{
$result->addPlayer('total_xp', $buf->readFloat32());
$result->addPlayer('teamname', $buf->readString());
$result->addPlayer('total_kills', $buf->readInt32());
$result->addPlayer('total_deaths', $buf->readInt32());
}
// @todo: Add team stuff
// Free some memory
unset($id);
}
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* F.E.A.R. Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Fear extends GameQ_Protocols_Gamespy2
{
protected $name = "fear";
protected $name_long = "F.E.A.R.";
protected $port = 27888;
}

View File

@ -0,0 +1,28 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Fortress Forever Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Ffe extends GameQ_Protocols_Source
{
protected $name = "ffe";
protected $name_long = "Fortress Forever";
}

View File

@ -0,0 +1,246 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Frontlines: Fuel of War Protocol Class
*
* Class is incomplete due to the lack of servers with players active.
*
* http://wiki.hlsw.net/index.php/FFOW_Protocol
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Ffow extends GameQ_Protocols
{
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @var array
*/
protected $packets = array(
self::PACKET_CHALLENGE => "\xFF\xFF\xFF\xFF\x57",
self::PACKET_RULES => "\xFF\xFF\xFF\xFF\x56%s",
//self::PACKET_PLAYERS => "\xFF\xFF\xFF\xFF\x55%s",
self::PACKET_INFO => "\xFF\xFF\xFF\xFF\x46\x4C\x53\x51",
);
protected $state = self::STATE_TESTING;
/**
* Set the packet mode to linear
*
* @var string
*/
protected $packet_mode = self::PACKET_MODE_LINEAR;
/**
* Methods to be run when processing the response(s)
*
* @var array
*/
protected $process_methods = array(
"process_info",
"process_rules",
//"process_players",
);
/**
* Default port for this server type
*
* @var int
*/
protected $port = 5478; // Default port, used if not set when instanced
/**
* The protocol being used
*
* @var string
*/
protected $protocol = 'ffow';
/**
* String name of this protocol class
*
* @var string
*/
protected $name = 'ffow';
/**
* Longer string name of this protocol class
*
* @var string
*/
protected $name_long = "Frontlines: Fuel of War";
/*
* Internal methods
*/
/**
* Parse the challenge response and apply it to all the packet types
* that require it.
*
* @see GameQ_Protocols_Core::parseChallengeAndApply()
*/
protected function parseChallengeAndApply()
{
// Skip the header
$this->challenge_buffer->skip(5);
// Apply the challenge and return
return $this->challengeApply($this->challenge_buffer->read(4));
}
/**
* Preprocess the server info packet(s)
*
* @param unknown_type $packets
*/
protected function preProcess_info($packets=array())
{
// Implode and return
return implode('', $packets);
}
protected function process_info()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_INFO))
{
return array();
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Parse the response
$data = $this->preProcess_info($this->packets_response[self::PACKET_INFO]);
// Create a new buffer
$buf = new GameQ_Buffer($data);
// Skip Header
$buf->skip(6);
$result->add('servername', $buf->readString());
$result->add('mapname', $buf->readString());
$result->add('modname', $buf->readString());
$result->add('gamemode', $buf->readString());
$result->add('description', $buf->readString());
$result->add('version', $buf->readString());
$result->add('port', $buf->readInt16());
$result->add('num_players', $buf->readInt8());
$result->add('max_players', $buf->readInt8());
$result->add('dedicated', $buf->readInt8());
$result->add('os', $buf->readInt8());
$result->add('password', $buf->readInt8());
$result->add('anticheat', $buf->readInt8());
$result->add('average_fps', $buf->readInt8());
$result->add('round', $buf->readInt8());
$result->add('max_rounds', $buf->readInt8());
$result->add('time_left', $buf->readInt16());
unset($buf, $data);
// Return the result
return $result->fetch();
}
/**
* Preprocess the rule packets returned. Not sure if this is final, need server to test against.
*
* @param array $packets
*/
protected function preProcess_rules($packets=array())
{
// Implode and return
return implode('', $packets);
}
/**
* Process the rules and return the data result
*/
protected function process_rules()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_RULES))
{
return array();
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Parse the response
$data = $this->preProcess_rules($this->packets_response[self::PACKET_RULES]);
// Create a new buffer
$buf = new GameQ_Buffer($data);
// Skip Header
$buf->skip(6);
while($buf->getLength())
{
$key = $buf->readString();
if(strlen($key) == 0)
{
break;
}
// Check for map
if(strstr($key, "Map:"))
{
$result->addSub("maplist", "name", $buf->readString());
}
else // Regular rule
{
$result->add($key, $buf->readString());
}
}
unset($buf, $data);
// Return the result
return $result->fetch();
}
/**
* Pre process the player packets, Not final. Need server to test against
*
* @param array $packets
*/
protected function preProcess_players($packets=array())
{
// Implode and return
return implode('', $packets);
}
protected function process_players()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_PLAYERS))
{
return array();
}
return array();
}
}

View File

@ -0,0 +1,216 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* GameSpy Protocol Class
*
* This class is used as the basis for all game servers
* that use the GameSpy protocol for querying
* server status.
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Gamespy extends GameQ_Protocols
{
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* Note: We only send the status packet since that has all the information we ever need.
* The other packets are left for reference purposes
*
* @var array
*/
protected $packets = array(
self::PACKET_STATUS => "\x5C\x73\x74\x61\x74\x75\x73\x5C",
//self::PACKET_PLAYERS => "\x5C\x70\x6C\x61\x79\x65\x72\x73\x5C",
//self::PACKET_DETAILS => "\x5C\x69\x6E\x66\x6F\x5C",
//self::PACKET_BASIC => "\x5C\x62\x61\x73\x69\x63\x5C",
//self::PACKET_RULES => "\x5C\x72\x75\x6C\x65\x73\x5C",
);
/**
* Methods to be run when processing the response(s)
*
* @var array
*/
protected $process_methods = array(
"process_status",
);
/**
* Default port for this server type
*
* @var int
*/
protected $port = 1; // Default port, used if not set when instanced
/**
* The protocol being used
*
* @var string
*/
protected $protocol = 'gamespy';
/**
* String name of this protocol class
*
* @var string
*/
protected $name = 'gamespy';
/**
* Longer string name of this protocol class
*
* @var string
*/
protected $name_long = "Gamespy";
/*
* Internal methods
*/
protected function preProcess($packets)
{
// Only one packet so its in order
if (count($packets) == 1)
{
return $packets[0];
}
// Holds the new list of packets, which will be stripped of queryid and ordered properly.
$packets_ordered = array();
// Loop thru the packets
foreach ($packets as $packet)
{
// Check to see if we had a preg_match error
if(preg_match("#^(.*)\\\\queryid\\\\([^\\\\]+)(\\\\|$)#", $packet, $matches) === FALSE)
{
throw new GameQ_ProtocolsException('An error occured while parsing the status packets');
return $packets_ordered;
}
// Lets make the key proper incase of decimal points
if(strstr($matches[2], '.'))
{
list($req_id, $req_num) = explode('.', $matches[2]);
// Now lets put back the number but make sure we pad the req_num so it is correct
// Should make sure the length is always 4 digits past the decimal point
// For some reason the req_num is 1->12.. instead of 01->12 ... so it doesnt ksort properly
$key = $req_id . sprintf(".%04s", $req_num);
}
else
{
$key = $matches[2];
}
// Add this stripped queryid to the new array with the id as the key
$packets_ordered[$key] = $matches[1];
}
// Sort the new array to make sure the keys (query ids) are in the proper order
ksort($packets_ordered, SORT_NUMERIC);
// Implode and return only the values as we dont care about the keys anymore
return implode('', array_values($packets_ordered));
}
/**
* Process the server status
*
* @throws GameQ_ProtocolsException
*/
protected function process_status()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_STATUS))
{
return array();
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Lets pre process and make sure these things are in the proper order by id
$data = $this->preProcess($this->packets_response[self::PACKET_STATUS]);
// Create a new buffer
$buf = new GameQ_Buffer($data);
// Lets peek and see if the data starts with a \
if($buf->lookAhead(1) == '\\')
{
// Burn the first one
$buf->skip(1);
}
// Explode the data
$data = explode('\\', $buf->getBuffer());
// Remove the last 2 "items" as it should be final\
array_pop($data);
array_pop($data);
// Init some vars
$num_players = 0;
$num_teams = 0;
// Now lets loop the array
for($x=0;$x<count($data);$x+=2)
{
// Set some local vars
$key = $data[$x];
$val = $data[$x+1];
// Check for <variable>_<count> variable (i.e players)
if(($suffix = strrpos($key, '_')) !== FALSE && is_numeric(substr($key, $suffix+1)))
{
// See if this is a team designation
if(substr($key, 0, $suffix) == 'teamname')
{
$result->addTeam('teamname', $val);
$num_teams++;
}
else // Its a player
{
if(substr($key, 0, $suffix) == 'playername')
{
$num_players++;
}
$result->addPlayer(substr($key, 0, $suffix), $val);
}
}
else // Regular variable so just add the value.
{
$result->add($key, $val);
}
}
// Add the player and team count
$result->add('num_players', $num_players);
$result->add('num_teams', $num_teams);
unset($buf, $data, $key, $val, $suffix, $x);
return $result->fetch();
}
}

View File

@ -0,0 +1,261 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* GameSpy2 Protocol Class
*
* This class is used as the basis for all game servers
* that use the GameSpy2 protocol for querying
* server status.
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Gamespy2 extends GameQ_Protocols
{
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @var array
*/
protected $packets = array(
self::PACKET_DETAILS => "\xFE\xFD\x00\x43\x4F\x52\x59\xFF\x00\x00",
self::PACKET_PLAYERS => "\xFE\xFD\x00\x43\x4F\x52\x59\x00\xFF\xFF",
);
/**
* Methods to be run when processing the response(s)
*
* @var array
*/
protected $process_methods = array(
"process_details",
"process_players",
);
/**
* Default port for this server type
*
* @var int
*/
protected $port = 1; // Default port, used if not set when instanced
/**
* The protocol being used
*
* @var string
*/
protected $protocol = 'gamespy2';
/**
* String name of this protocol class
*
* @var string
*/
protected $name = 'gamespy2';
/**
* Longer string name of this protocol class
*
* @var string
*/
protected $name_long = "Gamespy2";
/*
* Internal methods
*/
/**
* Pre-process the server details data that was returned.
*
* @param array $packets
*/
protected function preProcess_details($packets)
{
return $packets[0];
}
/**
* Process the server details
*
* @throws GameQ_ProtocolsException
*/
protected function process_details()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_DETAILS))
{
return array();
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Let's preprocess the rules
$data = $this->preProcess_details($this->packets_response[self::PACKET_DETAILS]);
// Create a new buffer
$buf = new GameQ_Buffer($data);
// Make sure the data is formatted properly
if($buf->lookAhead(5) != "\x00\x43\x4F\x52\x59")
{
throw new GameQ_ProtocolsException("Data for ".__METHOD__." does not have the proper header. Header: ".$buf->lookAhead(5));
return false;
}
// Now verify the end of the data is correct
if($buf->readLast() !== "\x00")
{
throw new GameQ_ProtocolsException("Data for ".__METHOD__." does not have the proper ending. Ending: ".$buf->readLast());
return false;
}
// Skip the header
$buf->skip(5);
// Loop thru all of the settings and add them
while ($buf->getLength())
{
// Temp vars
$key = $buf->readString();
$val = $buf->readString();
// Check to make sure there is a valid pair
if(!empty($key))
{
$result->add($key, $val);
}
}
unset($buf, $data, $key, $var);
return $result->fetch();
}
/**
* Pre-process the player data that was returned.
*
* @param array $packets
*/
protected function preProcess_players($packets)
{
return $packets[0];
}
/**
* Process the player data
*
* @throws GameQ_ProtocolsException
*/
protected function process_players()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_PLAYERS))
{
return array();
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Let's preprocess the rules
$data = $this->preProcess_players($this->packets_response[self::PACKET_PLAYERS]);
// Create a new buffer
$buf = new GameQ_Buffer($data);
// Make sure the data is formatted properly
if($buf->lookAhead(6) != "\x00\x43\x4F\x52\x59\x00")
{
throw new GameQ_ProtocolsException("Data for ".__METHOD__." does not have the proper header. Header: ".$buf->lookAhead(6));
return false;
}
// Now verify the end of the data is correct
if($buf->readLast() !== "\x00")
{
throw new GameQ_ProtocolsException("Data for ".__METHOD__." does not have the proper ending. Ending: ".$buf->readLast());
return false;
}
// Skip the header
$buf->skip(6);
// Players are first
$this->parse_playerteam('players', $buf, $result);
// Teams are next
$this->parse_playerteam('teams', $buf, $result);
unset($buf, $data);
return $result->fetch();
}
/**
* Parse the player/team info returned from the player call
*
* @param string $type
* @param GameQ_Buffer $buf
* @param GameQ_Result $result
*/
protected function parse_playerteam($type, &$buf, &$result)
{
// Do count
$result->add('num_'.$type, $buf->readInt8());
// Variable names
$varnames = array();
// Loop until we run out of length
while ($buf->getLength())
{
$varnames[] = str_replace('_', '', $buf->readString());
if ($buf->lookAhead() === "\x00")
{
$buf->skip();
break;
}
}
// Check if there are any value entries
if ($buf->lookAhead() == "\x00")
{
$buf->skip();
return;
}
// Get the values
while ($buf->getLength() > 4)
{
foreach ($varnames as $varname)
{
$result->addSub($type, $varname, $buf->readString());
}
if ($buf->lookAhead() === "\x00")
{
$buf->skip();
break;
}
}
return;
}
}

View File

@ -0,0 +1,446 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* GameSpy3 Protocol Class
*
* This class is used as the basis for all game servers
* that use the GameSpy3 protocol for querying
* server status.
*
* Note: UT3 and Crysis2 have known issues with GSv3 responses
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Gamespy3 extends GameQ_Protocols
{
/**
* Set the packet mode to linear
*
* @var string
*/
protected $packet_mode = self::PACKET_MODE_LINEAR;
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @var array
*/
protected $packets = array(
self::PACKET_CHALLENGE => "\xFE\xFD\x09\x10\x20\x30\x40",
self::PACKET_ALL => "\xFE\xFD\x00\x10\x20\x30\x40%s\xFF\xFF\xFF\x01",
);
/**
* Methods to be run when processing the response(s)
*
* @var array
*/
protected $process_methods = array(
"process_all",
);
/**
* Default port for this server type
*
* @var int
*/
protected $port = 1; // Default port, used if not set when instanced
/**
* The protocol being used
*
* @var string
*/
protected $protocol = 'gamespy3';
/**
* String name of this protocol class
*
* @var string
*/
protected $name = 'gamespy3';
/**
* Longer string name of this protocol class
*
* @var string
*/
protected $name_long = "Gamespy3";
/**
* Parse the challenge response and apply it to all the packet types
* that require it.
*
* @see GameQ_Protocols_Core::parseChallengeAndApply()
*/
protected function parseChallengeAndApply()
{
// Pull out the challenge
$challenge = substr(preg_replace( "/[^0-9\-]/si", "", $this->challenge_buffer->getBuffer()), 1);
$challenge_result = sprintf(
"%c%c%c%c",
( $challenge >> 24 ),
( $challenge >> 16 ),
( $challenge >> 8 ),
( $challenge >> 0 )
);
// Apply the challenge and return
return $this->challengeApply($challenge_result);
}
/*
* Internal methods
*/
protected function preProcess_all($packets)
{
$return = array();
// Get packet index, remove header
foreach ($packets as $index => $packet)
{
// Make new buffer
$buf = new GameQ_Buffer($packet);
// Skip the header
$buf->skip(14);
// Get the current packet and make a new index in the array
$return[$buf->readInt16()] = $buf->getBuffer();
}
unset($buf);
// Sort packets, reset index
ksort($return);
// Grab just the values
$return = array_values($return);
// Compare last var of current packet with first var of next packet
// On a partial match, remove last var from current packet,
// variable header from next packet
for ($i = 0, $x = count($return); $i < $x - 1; $i++)
{
// First packet
$fst = substr($return[$i], 0, -1);
// Second packet
$snd = $return[$i+1];
// Get last variable from first packet
$fstvar = substr($fst, strrpos($fst, "\x00")+1);
// Get first variable from last packet
$snd = substr($snd, strpos($snd, "\x00")+2);
$sndvar = substr($snd, 0, strpos($snd, "\x00"));
// Check if fstvar is a substring of sndvar
// If so, remove it from the first string
if (strpos($sndvar, $fstvar) !== false)
{
$return[$i] = preg_replace("#(\\x00[^\\x00]+\\x00)$#", "\x00", $return[$i]);
}
}
// Now let's loop the return and remove any dupe prefixes
for($x = 1; $x < count($return); $x++)
{
$buf = new GameQ_Buffer($return[$x]);
$prefix = $buf->readString();
// Check to see if the return before has the same prefix present
if($prefix != null && strstr($return[($x-1)], $prefix))
{
// Update the return by removing the prefix plus 2 chars
$return[$x] = substr(str_replace($prefix, '', $return[$x]), 2);
}
unset($buf);
}
unset($x, $i, $snd, $sndvar, $fst, $fstvar);
// Implode into a string and return
return implode("", $return);
}
protected function process_all()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_ALL))
{
return array();
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Parse the response
$data = $this->preProcess_all($this->packets_response[self::PACKET_ALL]);
// Create a new buffer
$buf = new GameQ_Buffer($data);
// We go until we hit an empty key
while($buf->getLength())
{
$key = $buf->readString();
if (strlen($key) == 0)
{
break;
}
$result->add($key, $buf->readString());
}
// Now we need to offload to parse the remaining data, player and team information
$this->parsePlayerTeamInfo($buf, $result);
// Return the result
return $result->fetch();
}
protected function delete_result(&$result, $array)
{
foreach($array as $key)
{
unset($result[$key]);
}
return TRUE;
}
protected function move_result(&$result, $old, $new)
{
if (isset($result[$old]))
{
$result[$new] = $result[$old];
unset($result[$old]);
}
return TRUE;
}
/**
* Parse the player and team information but do it smartly. Update to the old parseSub method.
*
* @param GameQ_Buffer $buf
* @param GameQ_Result $result
*/
protected function parsePlayerTeamInfo(GameQ_Buffer &$buf, GameQ_Result &$result)
{
/*
* Explode the data into groups. First is player, next is team (item_t)
*
* Each group should be as follows:
*
* [0] => item_
* [1] => information for item_
* ...
*/
$data = explode("\x00\x00", $buf->getBuffer());
// By default item_group is blank, this will be set for each loop thru the data
$item_group = '';
// By default the item_type is blank, this will be set on each loop
$item_type = '';
// Loop through all of the $data for information and pull it out into the result
for($x=0; $x < count($data)-1; $x++)
{
// Pull out the item
$item = $data[$x];
// If this is an empty item, move on
if($item == '' || $item == "\x00")
{
continue;
}
/*
* Left as reference:
*
* Each block of player_ and team_t have preceeding junk chars
*
* player_ is actually \x01player_
* team_t is actually \x00\x02team_t
*
* Probably a by-product of the change to exploding the data from the original.
*
* For now we just strip out these characters
*/
// Check to see if $item has a _ at the end, this is player info
if(substr($item, -1) == '_')
{
// Set the item group
$item_group = 'players';
// Set the item type, rip off any trailing stuff and bad chars
$item_type = rtrim(str_replace("\x01", '', $item), '_');
}
// Check to see if $item has a _t at the end, this is team info
elseif(substr($item, -2) == '_t')
{
// Set the item group
$item_group = 'teams';
// Set the item type, rip off any trailing stuff and bad chars
$item_type = rtrim(str_replace(array("\x00", "\x02"), '', $item), '_t');
}
// We can assume it is data belonging to a previously defined item
else
{
// Make a temp buffer so we have easier access to the data
$buf_temp = new GameQ_Buffer($item);
// Get the values
while ($buf_temp->getLength())
{
// No value so break the loop, end of string
if (($val = $buf_temp->readString()) === '')
{
break;
}
// Add the value to the proper item in the correct group
$result->addSub($item_group, $item_type, trim($val));
}
// Unset out buffer
unset($buf_temp);
}
}
// Free up some memory
unset($data, $item, $item_group, $item_type, $val);
}
/**
* Parse the player and team info
*
* @param GameQ_Buffer $buf
* @param GameQ_Result $result
* @throws GameQ_ProtocolsException
* @return boolean
*/
protected function parsePlayerTeamInfoNew(GameQ_Buffer &$buf, GameQ_Result &$result)
{
/**
* Player info is always first, team info (if defined) is second.
*
* Reference:
*
* Player info is preceeded by a hex code of \x01
* Team info is preceeded by a hex code of \x02
*/
// Check the header to make sure the player data is proper
if($buf->read(1) != "\x01")
{
//throw new GameQ_ProtocolsException("First character in player buffer != '\x01'");
return FALSE;
}
// Offload the player parsing
$this->parseSubInfo('players', $buf->readString("\x00\x00\x00"), $result);
// Check to make sure we have team information
if($buf->getLength() >= 6)
{
// Burn chars
$buf->skip(2);
// Check the header to make sure the data is proper
if($buf->read(1) != "\x02")
{
//throw new GameQ_ProtocolsException("First character in team buffer != '\x02'");
return FALSE;
}
// Offload the team parsing
$this->parseSubInfo('teams', $buf->readString("\x00\x00\x00"), $result);
}
return TRUE;
}
/**
* Parse the sub-item information for players and teams
*
* @param string $section
* @param string $data
* @param GameQ_Result $result
* @return boolean
*/
protected function parseSubInfo($section, $data, GameQ_Result &$result)
{
/*
* Explode the items so we can iterate easier
*
* Items should split up as follows:
*
* [0] => item_
* [1] => data for item_
* [2] => item2_
* [3] => data for item2_
* ...
*/
$items = explode("\x00\x00", $data);
print_r($items);
// Loop through all of the items
for($x = 0; $x < count($items); $x += 2)
{
// $x is always the key for the item (i.e. player_, ping_, team_, score_, etc...)
$item_type = rtrim($items[$x], '_,_t');
// $x+1 is always the data for the above item
// Make a temp buffer so we have easier access to the data
$buf_temp = new GameQ_Buffer($items[$x+1]);
// Get the values
while ($buf_temp->getLength())
{
// No value so break the loop, end of string
if (($val = $buf_temp->readString()) === '')
{
break;
}
// Add the value to the proper item in the correct group
$result->addSub($section, $item_type, trim($val));
}
// Unset out buffer
unset($buf_temp, $val);
}
unset($x, $items, $item_type);
return TRUE;
}
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* GameSpy4 Protocol Class
*
* By all accounts GameSpy 4 seems to be GameSpy 3.
*
* References:
* http://www.deletedscreen.com/?p=951
* http://pastebin.com/2zZFDuTd
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Gamespy4 extends GameQ_Protocols_Gamespy3 {}

View File

@ -0,0 +1,28 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Garry's Mod Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Gmod extends GameQ_Protocols_Source
{
protected $name = "gmod";
protected $name_long = "Garry's Mod";
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Gore Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Gore extends GameQ_Protocols_Gamespy
{
protected $name = "gore";
protected $name_long = "Gore";
protected $port = 27778;
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Ghost Recon: Advanced Warfighter Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Graw extends GameQ_Protocols_Gamespy2
{
protected $name = "graw";
protected $name_long = "Ghost Recon: Advanced Warfighter";
protected $port = 15250;
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Ghost Recon: Advanced Warfighter 2 Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Graw2 extends GameQ_Protocols_Gamespy2
{
protected $name = "graw2";
protected $name_long = "Ghost Recon: Advanced Warfighter 2";
protected $port = 16250;
}

View File

@ -0,0 +1,28 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Half Life 2: Deathmatch Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Hl2dm extends GameQ_Protocols_Source
{
protected $name = "hl2dm";
protected $name_long = "Half Life 2: Deathmatch";
}

View File

@ -0,0 +1,28 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Half Life Deathmatch Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Hldm extends GameQ_Protocols_Source
{
protected $name = "hldm";
protected $name_long = "Half Life: Deathmatch";
}

View File

@ -0,0 +1,28 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Homefront Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Homefront extends GameQ_Protocols_Source
{
protected $name = "homefront";
protected $name_long = "Homefront";
}

View File

@ -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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Http Protocol Class
*
* Used for making actual http requests to servers for information
*
* @author Austin Bischoff <austin@codebeard.com>
*/
abstract class GameQ_Protocols_Http extends GameQ_Protocols
{
/**
* Set the transport to use TCP
*
* @var string
*/
protected $transport = self::TRANSPORT_TCP;
/**
* Default port for this server type
*
* @var int
*/
protected $port = 80; // Default port, used if not set when instanced
}

View File

@ -0,0 +1,28 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Insurgency Mod Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Insurgency extends GameQ_Protocols_Source
{
protected $name = "insurgency";
protected $name_long = "Insurgency";
}

View File

@ -0,0 +1,78 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Killing floor Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Killingfloor extends GameQ_Protocols_Unreal2
{
protected $name = "killingfloor";
protected $name_long = "Killing Floor";
protected $port = 7708;
/**
* Overloaded for Killing Floor servername issue, could be all unreal2 games though
*
* @see GameQ_Protocols_Unreal2::process_details()
*/
protected function process_details()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_DETAILS))
{
return array();
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Let's preprocess the rules
$data = $this->preProcess_details($this->packets_response[self::PACKET_DETAILS]);
// Create a buffer
$buf = new GameQ_Buffer($data);
$result->add('serverid', $buf->readInt32()); // 0
$result->add('serverip', $buf->readPascalString(1)); // empty
$result->add('gameport', $buf->readInt32());
$result->add('queryport', $buf->readInt32()); // 0
// We burn the first char since it is not always correct with the hostname
$buf->skip(1);
// Read as a regular string since the length is incorrect (what we skipped earlier)
$result->add('servername', $buf->readString());
// The rest is read as normal
$result->add('mapname', $buf->readPascalString(1));
$result->add('gametype', $buf->readPascalString(1));
$result->add('playercount', $buf->readInt32());
$result->add('maxplayers', $buf->readInt32());
$result->add('ping', $buf->readInt32()); // 0
// @todo: There is extra data after this point (~9 bytes), cant find any reference on what it is
unset($buf);
// Return the result
return $result->fetch();
}
}

View File

@ -0,0 +1,28 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Left 4 Dead Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_L4d extends GameQ_Protocols_Source
{
protected $name = "l4d";
protected $name_long = "Left 4 Dead";
}

View File

@ -0,0 +1,28 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Left 4 Dead 2 Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_L4d2 extends GameQ_Protocols_Source
{
protected $name = "l4d2";
protected $name_long = "Left 4 Dead 2";
}

View File

@ -0,0 +1,43 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Mincraft Protocol Class
*
* Thanks to https://github.com/xPaw/PHP-Minecraft-Query for helping me realize this is
* Gamespy 3 Protocol. Make sure you enable the items below for it to work.
*
* Information from original author:
* Instructions
*
* Before using this class, you need to make sure that your server is running GS4 status listener.
*
* Look for those settings in server.properties:
*
* enable-query=true
* query.port=25565
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Minecraft extends GameQ_Protocols_Gamespy4
{
protected $name = "minecraft";
protected $name_long = "Minecraft";
protected $port = 25565;
}

View File

@ -0,0 +1,133 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Minequery Protocol Class
*
* This class is used as the basis for all game servers
* that use the Minequery protocol for querying
* server status.
*
* Make sure you have Minequery running. Check the GameQ github wiki for specifics.
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Minequery extends GameQ_Protocols
{
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @var array
*/
protected $packets = array(
self::PACKET_STATUS => "QUERY_JSON\n",
);
/**
* Methods to be run when processing the response(s)
*
* @var array
*/
protected $process_methods = array(
"process_status",
);
/**
* Set the transport to use TCP
*
* @var string
*/
protected $transport = self::TRANSPORT_TCP;
/**
* Default port for this server type
*
* @var int
*/
protected $port = 25566; // Default port, used if not set when instanced
/**
* The protocol being used
*
* @var string
*/
protected $protocol = 'minequery';
/**
* String name of this protocol class
*
* @var string
*/
protected $name = 'minequery';
/**
* Longer string name of this protocol class
*
* @var string
*/
protected $name_long = "Minequery";
/*
* Internal methods
*/
/**
* Process the server status
*
* @throws GameQ_ProtocolsException
*/
protected function process_status()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_STATUS))
{
return array();
}
// The response should be a single string so just combine all the packets into a single string
$response = implode('', $this->packets_response[self::PACKET_STATUS]);
// Check to see if this is valid JSON.
if(($data = json_decode($response)) === NULL)
{
throw new GameQ_ProtocolsException('Unable to decode the JSON data for Minequery');
return FALSE;
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Server is always dedicated
$result->add('dedicated', TRUE);
// Add the address and port info
$result->add('serverport', $data->serverPort);
$result->add('numplayers', $data->playerCount);
$result->add('maxplayers', $data->maxPlayers);
// Do the players
foreach($data->playerList AS $i => $name)
{
$result->addPlayer('name', $name);
}
return $result->fetch();
}
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Medal of Honor: Allied Assault Protocol Class
*
* @author Marcel Bößendörfer <m.boessendoerfer@marbis.net>
*/
class GameQ_Protocols_Mohaa extends GameQ_Protocols_Gamespy
{
protected $name = "mohaa";
protected $name_long = "Medal of Honor: Allied Assault";
protected $port = 12300;
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Medal of Honor: Spearhead Protocol Class
*
* @author Marcel Bößendörfer <m.boessendoerfer@marbis.net>
*/
class GameQ_Protocols_Mohsh extends GameQ_Protocols_Gamespy
{
protected $name = "mohsh";
protected $name_long = "Medal of Honor: Spearhead";
protected $port = 12300;
}

View File

@ -0,0 +1,131 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Medal of Honor Warfighter Protocol Class
*
* MOHWF is the same as BF3 minus some quirks in the status query hence the extension
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Mohwf extends GameQ_Protocols_Bf3
{
/**
* The protocol being used
*
* @var string
*/
protected $protocol = 'mohwf';
/**
* String name of this protocol class
*
* @var string
*/
protected $name = 'mohwf';
/**
* Longer string name of this protocol class
*
* @var string
*/
protected $name_long = "Medal of Honor Warfighter";
/*
* Internal Methods
*/
protected function process_status()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_STATUS))
{
return array();
}
// Make buffer for data
$buf = new GameQ_Buffer($this->preProcess_status($this->packets_response[self::PACKET_STATUS]));
$buf->skip(8); /* skip header */
// Decode the words into an array so we can use this data
$words = $this->decodeWords($buf);
// Make sure we got OK
if (!isset ($words[0]) || $words[0] != 'OK')
{
throw new GameQ_ProtocolsException('Packet Response was not OK! Buffer:'.$buf->getBuffer());
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Server is always dedicated
$result->add('dedicated', TRUE);
// No mods, as of yet
$result->add('mod', FALSE);
// These are the same no matter what mode the server is in
$result->add('hostname', $words[1]);
$result->add('numplayers', $words[2]);
$result->add('maxplayers', $words[3]);
$result->add('gametype', $words[4]);
$result->add('map', $words[5]);
$result->add('roundsplayed', $words[6]);
$result->add('roundstotal', $words[7]);
// Figure out the number of teams
$num_teams = intval($words[8]);
// Set the current index
$index_current = 9;
// Loop for the number of teams found, increment along the way
for($id=1; $id<=$num_teams; $id++)
{
$result->addSub('teams', 'tickets', $words[$index_current]);
$result->addSub('teams', 'id', $id);
// Increment
$index_current++;
}
// Get and set the rest of the data points.
$result->add('targetscore', $words[$index_current]);
$result->add('online', TRUE); // Forced TRUE, it seems $words[$index_current + 1] is always empty
$result->add('ranked', $words[$index_current + 2] === 'true');
$result->add('punkbuster', $words[$index_current + 3] === 'true');
$result->add('password', $words[$index_current + 4] === 'true');
$result->add('uptime', $words[$index_current + 5]);
$result->add('roundtime', $words[$index_current + 6]);
// The next 3 are empty in MOHWF, kept incase they start to work some day
$result->add('ip_port', $words[$index_current + 7]);
$result->add('punkbuster_version', $words[$index_current + 8]);
$result->add('join_queue', $words[$index_current + 9] === 'true');
$result->add('region', $words[$index_current + 10]);
$result->add('pingsite', $words[$index_current + 11]);
$result->add('country', $words[$index_current + 12]);
unset($buf, $words);
return $result->fetch();
}
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Multi Theft Auto Protocol Class
*
* @author Marcel Bößendörfer <m.boessendoerfer@marbis.net>
*/
class GameQ_Protocols_Mta extends GameQ_Protocols_ASE
{
protected $name = "Mta";
protected $name_long = "Multi Theft Auto";
protected $port = 22126;
}

View File

@ -0,0 +1,28 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Natural Selection Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Ns extends GameQ_Protocols_Source
{
protected $name = "ns";
protected $name_long = "Natural Selection";
}

View File

@ -0,0 +1,37 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Natural Selection 2 Protocol Class
*
* Note that the query port is the server connect port + 1
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Ns2 extends GameQ_Protocols_Source
{
protected $name = "ns2";
protected $name_long = "Natural Selection 2";
/**
* Default port for this server type
*
* @var int
*/
protected $port = 27016;
}

View File

@ -0,0 +1,205 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Quake 2 Protocol Class
*
* This class is used as the basis for all game servers
* that use the Quake 2 protocol for querying
* server status.
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Quake2 extends GameQ_Protocols
{
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @var array
*/
protected $packets = array(
self::PACKET_STATUS => "\xFF\xFF\xFF\xFFstatus\x00",
);
/**
* Methods to be run when processing the response(s)
*
* @var array
*/
protected $process_methods = array(
"process_status",
);
/**
* Default port for this server type
*
* @var int
*/
protected $port = 27910; // Default port, used if not set when instanced
/**
* The protocol being used
*
* @var string
*/
protected $protocol = 'quake2';
/**
* String name of this protocol class
*
* @var string
*/
protected $name = 'quake2';
/**
* Longer string name of this protocol class
*
* @var string
*/
protected $name_long = "Quake 2";
/*
* Internal methods
*/
protected function preProcess_status($packets)
{
// Should only be one packet
if (count($packets) > 1)
{
throw new GameQ_ProtocolsException('Quake 2 status has more than 1 packet');
}
// Make buffer so we can check this out
$buf = new GameQ_Buffer($packets[0]);
// Grab the header
$header = $buf->read(11);
// Now lets verify the header
if($header != "\xFF\xFF\xFF\xFFprint\x0a\\")
{
throw new GameQ_ProtocolsException('Unable to match Gamespy 2 status response header. Header: '. $header);
return FALSE;
}
// Return the data with the header stripped, ready to go.
return $buf->getBuffer();
}
/**
* Process the server status
*
* @throws GameQ_ProtocolsException
*/
protected function process_status()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_STATUS))
{
return array();
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Lets pre process and make sure these things are in the proper order by id
$data = $this->preProcess_status($this->packets_response[self::PACKET_STATUS]);
// Make buffer
$buf = new GameQ_Buffer($data);
// First section is the server info, the rest is player info
$server_info = $buf->readString("\x0A");
$player_info = $buf->getBuffer();
unset($buf);
// Make a new buffer for the server info
$buf_server = new GameQ_Buffer($server_info);
// Key / value pairs
while ($buf_server->getLength())
{
$result->add(
$buf_server->readString('\\'),
$buf_server->readStringMulti(array('\\', "\x0a"), $delimfound)
);
if ($delimfound === "\x0a")
{
break;
}
}
// Now send the rest to players
$this->parsePlayers($result, $player_info);
// Free some memory
unset($sections, $player_info, $server_info, $delimfound, $buf_server, $data);
// Return the result
return $result->fetch();
}
/**
* Parse the players and add them to the return.
*
* This is overloadable because it seems that different games return differen info.
*
* @param GameQ_Result $result
* @param string $players_info
*/
protected function parsePlayers(GameQ_Result &$result, $players_info)
{
// Explode the arrays out
$players = explode("\x0A", $players_info);
// Remove the last array item as it is junk
array_pop($players);
// Add total number of players
$result->add('num_players', count($players));
// Loop the players
foreach($players AS $player_info)
{
$buf = new GameQ_Buffer($player_info);
// Add player info
$result->addPlayer('frags', $buf->readString("\x20"));
$result->addPlayer('ping', $buf->readString("\x20"));
// Skip first "
$buf->skip(1);
// Add player name
$result->addPlayer('name', trim($buf->readString('"')));
// Skip first "
$buf->skip(2);
// Add address
$result->addPlayer('address', trim($buf->readString('"')));
}
// Free some memory
unset($buf, $players, $player_info);
}
}

View File

@ -0,0 +1,200 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Quake 3 Protocol Class
*
* This class is used as the basis for all game servers
* that use the Quake 3 protocol for querying
* server status.
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Quake3 extends GameQ_Protocols
{
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @var array
*/
protected $packets = array(
self::PACKET_STATUS => "\xFF\xFF\xFF\xFF\x67\x65\x74\x73\x74\x61\x74\x75\x73\x0A",
//self::PACKET_DETAILS => "\xFF\xFF\xFF\xFFgetinfo\x00",
);
/**
* Methods to be run when processing the response(s)
*
* @var array
*/
protected $process_methods = array(
"process_status",
);
/**
* Default port for this server type
*
* @var int
*/
protected $port = 27960; // Default port, used if not set when instanced
/**
* The protocol being used
*
* @var string
*/
protected $protocol = 'quake3';
/**
* String name of this protocol class
*
* @var string
*/
protected $name = 'quake3';
/**
* Longer string name of this protocol class
*
* @var string
*/
protected $name_long = "Quake 3";
/*
* Internal methods
*/
protected function preProcess_status($packets)
{
// Should only be one packet
if (count($packets) > 1)
{
throw new GameQ_ProtocolsException('Quake 3 status has more than 1 packet');
}
// Make buffer so we can check this out
$buf = new GameQ_Buffer($packets[0]);
// Grab the header
$header = $buf->read(20);
// Now lets verify the header
if($header != "\xFF\xFF\xFF\xFFstatusResponse\x0A\x5C")
{
throw new GameQ_ProtocolsException('Unable to match Gamespy 3 challenge response header. Header: '. $header);
return FALSE;
}
// Return the data with the header stripped, ready to go.
return $buf->getBuffer();
}
/**
* Process the server status
*
* @throws GameQ_ProtocolsException
*/
protected function process_status()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_STATUS))
{
return array();
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Lets pre process and make sure these things are in the proper order by id
$data = $this->preProcess_status($this->packets_response[self::PACKET_STATUS]);
// Make buffer
$buf = new GameQ_Buffer($data);
// First section is the server info, the rest is player info
$server_info = $buf->readString("\x0A");
$player_info = $buf->getBuffer();
unset($buf);
// Make a new buffer for the server info
$buf_server = new GameQ_Buffer($server_info);
// Key / value pairs
while ($buf_server->getLength())
{
$result->add(
$buf_server->readString('\\'),
$buf_server->readStringMulti(array('\\', "\x0a"), $delimfound)
);
if ($delimfound === "\x0a")
{
break;
}
}
// Now send the rest to players
$this->parsePlayers($result, $player_info);
// Free some memory
unset($sections, $player_info, $server_info, $delimfound, $buf_server, $data);
// Return the result
return $result->fetch();
}
/**
* Parse the players and add them to the return.
*
* This is overloadable because it seems that different games return differen info.
*
* @param GameQ_Result $result
* @param string $players_info
*/
protected function parsePlayers(GameQ_Result &$result, $players_info)
{
// Explode the arrays out
$players = explode("\x0A", $players_info);
// Remove the last array item as it is junk
array_pop($players);
// Add total number of players
$result->add('num_players', count($players));
// Loop the players
foreach($players AS $player_info)
{
$buf = new GameQ_Buffer($player_info);
// Add player info
$result->addPlayer('frags', $buf->readString("\x20"));
$result->addPlayer('ping', $buf->readString("\x20"));
// Skip first "
$buf->skip(1);
// Add player name
$result->addPlayer('name', trim($buf->readString('"')));
}
// Free some memory
unset($buf, $players, $player_info);
}
}

View File

@ -0,0 +1,44 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Quake 4 Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Quake4 extends GameQ_Protocols_Doom3
{
protected $name = "quake4";
protected $name_long = "Quake 4";
protected $port = 28004;
protected function parsePlayers(GameQ_Buffer &$buf, GameQ_Result &$result)
{
while (($id = $buf->readInt8()) != 32)
{
$result->addPlayer('id', $id);
$result->addPlayer('ping', $buf->readInt16());
$result->addPlayer('rate', $buf->readInt32());
$result->addPlayer('name', $buf->readString());
$result->addPlayer('clantag', $buf->readString());
}
return true;
}
}

View File

@ -0,0 +1,212 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Red Eclipse
*
* This game is based off of Cube 2 but the protocol response is way
* different than Cube 2
*
* Thanks to Poil for information to help build out this protocol class
*
* References:
* https://github.com/stainsby/redflare/blob/master/poller.js
* https://github.com/stainsby/redflare/blob/master/lib/protocol.js
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Redeclipse extends GameQ_Protocols_Cube2
{
/**
* The query protocol used to make the call
*
* @var string
*/
protected $protocol = 'redeclipse';
/**
* String name of this protocol class
*
* @var string
*/
protected $name = 'redeclipse';
/**
* Longer string name of this protocol class
*
* @var string
*/
protected $name_long = "Red Eclipse";
/**
* Defined Game mutators
*
* @var array
*/
protected $mutators = array(
'multi' => 1,
'ffa' => 2,
'coop' => 4,
'insta' => 8,
'medieval' => 16,
'kaboom' => 32,
'duel' => 64,
'survivor' => 128,
'classic' => 256,
'onslaught' => 512,
'jetpack' => 1024,
'vampire' => 2048,
'expert' => 4096,
'resize' => 8192,
);
/**
* Defined Master modes (i.e access restrictions)
*
* @var array
*/
protected $mastermodes = array(
'open', // 0
'veto', // 1
'locked', // 2
'private', // 3
'password', // 4
);
/**
* Defined Game modes
*
* @var array
*/
protected $gamemodes = array(
'demo', // 0
'edit', // 1
'deathmatch', // 2
'capture-the-flag', // 3
'defend-the-flag', // 4
'bomberball', // 5
'time-trial', // 6
'gauntlet' // 7
);
/**
* Process the status result. This result is different from the parent
*
* @see GameQ_Protocols_Cube2::process_status()
*/
protected function process_status()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_STATUS))
{
return array();
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Let's preprocess the rules
$data = $this->preProcess_status($this->packets_response[self::PACKET_STATUS]);
// Create a new buffer
$buf = new GameQ_Buffer($data);
// Check the header, should be the same response as the packet we sent
if($buf->read(6) != $this->packets[self::PACKET_STATUS])
{
throw new GameQ_ProtocolsException("Data for ".__METHOD__." does not have the proper header type (should be {$this->packets[self::PACKET_STATUS]}).");
return array();
}
/**
* Reference chart for ints by position
*
* 0 - Num players
* 1 - Number of items to follow (i.e. 8), not used yet
* 2 - Version
* 3 - gamemode (dm, ctf, etc...)
* 4 - mutators (sum of power of 2)
* 5 - Time remaining
* 6 - max players
* 7 - Mastermode (open, password, etc)
* 8 - variable count
* 9 - modification count
*/
$result->add('num_players', $this->readInt($buf));
$items = $this->readInt($buf); // We dump this as we dont use it for now
$result->add('version', $this->readInt($buf));
$result->add('gamemode', $this->gamemodes[$this->readInt($buf)]);
// This is a sum of power's of 2 (2^1, 1 << 1)
$mutators_number = $this->readInt($buf);
$mutators = array();
foreach($this->mutators AS $mutator => $flag)
{
if($flag & $mutators_number)
{
$mutators[] = $mutator;
}
}
$result->add('mutators', $mutators);
$result->add('mutators_number', $mutators_number);
$result->add('time_remaining', $this->readInt($buf));
$result->add('max_players', $this->readInt($buf));
$mastermode = $this->readInt($buf);
$result->add('mastermode', $this->mastermodes[$mastermode]);
$result->add('password', ((in_array($mastermode, array(4)))?TRUE:FALSE));
// @todo: No idea what these next 2 are used for
$result->add('variableCount', $this->readInt($buf));
$result->add('modificationCount', $this->readInt($buf));
$result->add('map', $buf->readString());
$result->add('servername', $buf->readString());
// The rest from here is player information, we read until we run out of strings (\x00)
while($raw = $buf->readString())
{
// Items seem to be seperated by \xc
$items = explode("\xc", $raw);
// Indexes 0, 1 & 5 seem to be junk
// Indexes 2, 3, 4 seem to have something of use, not sure about #3
$result->addPlayer('guid', (int) trim($items[2], "[]"));
// Index 4 has the player name with some kind int added on to the front, icon or something?
// Anyway remove it for now...
if(preg_match('/(\[[0-9]+\])(.*)/i', $items[4], $name))
{
$result->addPlayer('name', $name[2]);
}
}
unset($buf, $data);
return $result->fetch();
}
}

View File

@ -0,0 +1,121 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Red Faction Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Redfaction extends GameQ_Protocols
{
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @var array
*/
protected $packets = array(
self::PACKET_STATUS => "\x00\x00\x00\x00",
);
protected $state = self::STATE_TESTING;
/**
* Methods to be run when processing the response(s)
*
* @var array
*/
protected $process_methods = array(
"process_status",
);
/**
* Default port for this server type
*
* @var int
*/
protected $port = 7755; // Default port, used if not set when instanced
/**
* The protocol being used
*
* @var string
*/
protected $protocol = 'redfaction';
/**
* String name of this protocol class
*
* @var string
*/
protected $name = 'redfaction';
/**
* Longer string name of this protocol class
*
* @var string
*/
protected $name_long = "Red Faction";
/*
* Internal methods
*/
/**
* Process the server status
*
* @throws GameQ_ProtocolsException
*/
protected function process_status()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_STATUS))
{
return array();
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Header, we're being carefull here
if ($buf->read() !== "\x00")
{
throw new GameQ_ProtocolsException('Header error in Red Faction');
return FALSE;
}
// Dunno
while ($buf->read() !== "\x00") {}
$buf->read();
// Data
$result->add('servername', $buf->readString());
$result->add('gametype', $buf->readInt8());
$result->add('num_players', $buf->readInt8());
$result->add('max_players', $buf->readInt8());
$result->add('map', $buf->readString());
$buf->read();
$result->add('dedicated', $buf->readInt8());
// Free some memory
unset($sections, $buf, $data);
// Return the result
return $result->fetch();
}
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Red Orchestra: Ostfront 41-45 Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Redorchestra extends GameQ_Protocols_Gamespy
{
protected $name = "redorchestra";
protected $name_long = "Red Orchestra: Ostfront 41-45";
protected $port = 7767;
}

View File

@ -0,0 +1,32 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Red Orchestra 2 Protocol Class
*
* Thanks to http://forums.tripwireinteractive.com/showthread.php?t=72439 for information about the protocol
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Redorchestra2 extends GameQ_Protocols_Source
{
protected $name = "redorchestra2";
protected $name_long = "Red Orchestra 2";
protected $port = 27015;
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Return to Castle Wolfenstein Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Rtcw extends GameQ_Protocols_Quake3
{
protected $name = "rtcw";
protected $name_long = "Return to Castle Wolfenstein";
protected $port = 27960;
}

View File

@ -0,0 +1,239 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* San Andreas Multiplayer Protocol Class
*
* This class holds the query info and processing for SAMP
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Samp extends GameQ_Protocols
{
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @var array
*/
protected $packets = array(
self::PACKET_STATUS => "SAMP%s%si",
self::PACKET_PLAYERS => "SAMP%s%sd",
self::PACKET_RULES => "SAMP%s%sr",
);
/**
* Methods to be run when processing the response(s)
*
* @var array
*/
protected $process_methods = array(
"process_status",
"process_players",
"process_rules",
);
/**
* Default port for this server type
*
* @var int
*/
protected $port = 7777; // Default port, used if not set when instanced
/**
* The protocol being used
*
* @var string
*/
protected $protocol = 'samp';
/**
* String name of this protocol class
*
* @var string
*/
protected $name = 'samp';
/**
* Longer string name of this protocol class
*
* @var string
*/
protected $name_long = "San Andreas Multiplayer";
/*
* Internal methods
*/
/**
* We need to modify the packets before they are sent for this protocol
*
* @see GameQ_Protocols_Core::beforeSend()
*/
public function beforeSend()
{
// We need to repack the IP address of the server
$address = implode('', array_map('chr', explode('.', $this->ip)));
// Repack the server port
$port = pack ("S", $this->port);
// Let's loop the packets and set the proper pieces
foreach($this->packets AS $packet_type => $packet)
{
// Fill out the packet with the server info
$this->packets[$packet_type] = sprintf($packet, $address, $port);
}
// Free up some memory
unset($address, $port);
return TRUE;
}
protected function preProcess($packets)
{
// Make buffer so we can check this out
$buf = new GameQ_Buffer(implode('', $packets));
// Grab the header
$header = $buf->read(11);
// Now lets verify the header
if(substr($header, 0, 4) != "SAMP")
{
throw new GameQ_ProtocolsException('Unable to match SAMP response header. Header: '. $header);
return FALSE;
}
// Return the data with the header stripped, ready to go.
return $buf->getBuffer();
}
/**
* Process the server status
*
* @throws GameQ_ProtocolsException
*/
protected function process_status()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_STATUS))
{
return array();
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Always dedicated
$result->add('dedicated', TRUE);
// Preprocess and make buffer
$buf = new GameQ_Buffer($this->preProcess($this->packets_response[self::PACKET_STATUS]));
// Pull out the server information
$result->add('password', (bool) $buf->readInt8());
$result->add('num_players', $buf->readInt16());
$result->add('max_players', $buf->readInt16());
// These are read differently for these last 3
$result->add('servername', $buf->read($buf->readInt32()));
$result->add('gametype', $buf->read($buf->readInt32()));
$result->add('map', $buf->read($buf->readInt32()));
// Free some memory
unset($buf);
// Return the result
return $result->fetch();
}
/**
* Process server rules
*/
protected function process_rules()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_RULES))
{
return array();
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Preprocess and make buffer
$buf = new GameQ_Buffer($this->preProcess($this->packets_response[self::PACKET_RULES]));
// Number of rules
$result->add('num_rules', $buf->readInt16());
// Run until we run out of buffer
while ($buf->getLength())
{
$result->add($buf->readPascalString(), $buf->readPascalString());
}
// Free some memory
unset($buf);
// Return the result
return $result->fetch();
}
/**
* Process the players
*
* NOTE: There is a restriction on the SAMP server side that if there are too many players
* the player return will be empty. Nothing can really be done about this unless you bug
* the game developers to fix it.
*/
protected function process_players()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_PLAYERS))
{
return array();
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Preprocess and make buffer
$buf = new GameQ_Buffer($this->preProcess($this->packets_response[self::PACKET_PLAYERS]));
// Number of players
$result->add('num_players', $buf->readInt16());
// Run until we run out of buffer
while ($buf->getLength())
{
$result->addPlayer('id', $buf->readInt8());
$result->addPlayer('name', $buf->readPascalString());
$result->addPlayer('score', $buf->readInt32());
$result->addPlayer('ping', $buf->readInt32());
}
// Free some memory
unset($buf);
// Return the result
return $result->fetch();
}
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Soldier of Fortune 2 Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Sof2 extends GameQ_Protocols_Quake3
{
protected $name = "sof2";
protected $name_long = "Soldier of Fortune 2";
protected $port = 20100;
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Soldat Protocol Class
*
* @author Marcel Bößendörfer <m.boessendoerfer@marbis.net>
*/
class GameQ_Protocols_Soldat extends GameQ_Protocols_ASE
{
protected $name = "Soldat";
protected $name_long = "Soldat";
protected $port = 23196;
}

View File

@ -0,0 +1,453 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Valve Source Engine Protocol Class
*
* This class is used as the basis for all other source based servers
* that rely on the source protocol for game querying
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Source extends GameQ_Protocols
{
/*
* Source engine type constants
*/
const SOURCE_ENGINE = 0;
const GOLDSOURCE_ENGINE = 1;
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @var array
*/
protected $packets = array(
self::PACKET_CHALLENGE => "\xFF\xFF\xFF\xFF\x56\x00\x00\x00\x00",
self::PACKET_DETAILS => "\xFF\xFF\xFF\xFFTSource Engine Query\x00",
self::PACKET_PLAYERS => "\xFF\xFF\xFF\xFF\x55%s",
self::PACKET_RULES => "\xFF\xFF\xFF\xFF\x56%s",
);
/**
* Methods to be run when processing the response(s)
*
* @var array
*/
protected $process_methods = array(
"process_details",
"process_players",
"process_rules",
);
/**
* Default port for this server type
*
* @var int
*/
protected $port = 27015; // Default port, used if not set when instanced
/**
* The query protocol used to make the call
*
* @var string
*/
protected $protocol = 'source';
/**
* String name of this protocol class
*
* @var string
*/
protected $name = 'source';
/**
* Longer string name of this protocol class
*
* @var string
*/
protected $name_long = "Source Server";
/**
* Define the Source engine type. By default it is assumed to be Source
*
* @var int
*/
protected $source_engine = self::SOURCE_ENGINE;
/**
* Parse the challenge response and apply it to all the packet types
* that require it.
*
* @see GameQ_Protocols_Core::parseChallengeAndApply()
*/
protected function parseChallengeAndApply()
{
// Skip the header
$this->challenge_buffer->skip(5);
// Apply the challenge and return
return $this->challengeApply($this->challenge_buffer->read(4));
}
/*
* Internal methods
*/
/**
* Pre-process the server details data that was returned.
*
* @param array $packets
*/
protected function preProcess_details($packets)
{
// Process the packets
return $this->process_packets($packets);
}
/**
* Handles processing the details data into a usable format
*
* @throws GameQ_ProtocolsException
*/
protected function process_details()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_DETAILS))
{
return array();
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Let's preprocess the rules
$data = $this->preProcess_details($this->packets_response[self::PACKET_DETAILS]);
// Create a new buffer
$buf = new GameQ_Buffer($data);
// Skip the header (0xFF0xFF0xFF0xFF)
$buf->skip(4);
// Get the type
$type = $buf->read(1);
// Make sure the data is formatted properly
// Source is 0x49, Goldsource is 0x6d, 0x44 I am not sure about
if(!in_array($type, array("\x49", "\x44", "\x6d")))
{
throw new GameQ_ProtocolsException("Data for ".__METHOD__." does not have the proper header type (should be 0x49|0x44|0x6d). Header type: 0x".bin2hex($type));
return array();
}
// Update the engine type for other calls and other methods, if necessary
if(bin2hex($type) == '6d')
{
$this->source_engine = self::GOLDSOURCE_ENGINE;
}
// Check engine type
if ($this->source_engine == self::GOLDSOURCE_ENGINE)
{
$result->add('address', $buf->readString());
}
else
{
$result->add('protocol', $buf->readInt8());
}
$result->add('hostname', $buf->readString());
$result->add('map', $buf->readString());
$result->add('game_dir', $buf->readString());
$result->add('game_descr', $buf->readString());
// Check engine type
if ($this->source_engine != self::GOLDSOURCE_ENGINE)
{
$result->add('steamappid', $buf->readInt16());
}
$result->add('num_players', $buf->readInt8());
$result->add('max_players', $buf->readInt8());
// Check engine type
if ($this->source_engine == self::GOLDSOURCE_ENGINE)
{
$result->add('version', $buf->readInt8());
}
else
{
$result->add('num_bots', $buf->readInt8());
}
$result->add('dedicated', $buf->read());
$result->add('os', $buf->read());
$result->add('password', $buf->readInt8());
// Check engine type
if ($this->source_engine == self::GOLDSOURCE_ENGINE)
{
$result->add('ismod', $buf->readInt8());
}
$result->add('secure', $buf->readInt8());
// Check engine type
if ($this->source_engine == self::GOLDSOURCE_ENGINE)
{
$result->add('num_bots', $buf->readInt8());
}
else
{
$result->add('version', $buf->readInt8());
}
// Add extra data flag check here, only for source games (not goldsource)
// https://developer.valvesoftware.com/wiki/Server_Queries#Source_servers_2
unset($buf);
return $result->fetch();
}
/**
* Pre-process the player data sent
*
* @param array $packets
*/
protected function preProcess_players($packets)
{
// Process the packets
return $this->process_packets($packets);
}
/**
* Handles processing the player data into a useable format
*
* @throws GameQ_ProtocolsException
*/
protected function process_players()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_PLAYERS))
{
return array();
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Let's preprocess the rules
$data = $this->preProcess_players($this->packets_response[self::PACKET_PLAYERS]);
// Create a new buffer
$buf = new GameQ_Buffer($data);
// Make sure the data is formatted properly
if(($header = $buf->read(5)) != "\xFF\xFF\xFF\xFF\x44")
{
throw new GameQ_ProtocolsException("Data for ".__METHOD__." does not have the proper header (should be 0xFF0xFF0xFF0xFF0x44). Header: ".bin2hex($header));
return array();
}
// Pull out the number of players
$num_players = $buf->readInt8();
// Player count
$result->add('num_players', $num_players);
// No players so no need to look any further
if($num_players == 0)
{
return $result->fetch();
}
// Players list
while ($buf->getLength())
{
$result->addPlayer('id', $buf->readInt8());
$result->addPlayer('name', $buf->readString());
$result->addPlayer('score', $buf->readInt32Signed());
$result->addPlayer('time', $buf->readFloat32());
}
unset($buf);
return $result->fetch();
}
/**
* Pre process the rules data that was returned. Make sure the return
* data is in a single string
*
* @param array $packets
*/
protected function preProcess_rules($packets)
{
// Process the packets
return $this->process_packets($packets);
}
/**
* Handles processing the rules data into a usable format
*
* @throws GameQ_ProtocolsException
*/
protected function process_rules()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_RULES))
{
return array();
}
// Set the result to a new result instance
$result = new GameQ_Result();
// Let's preprocess the rules
$data = $this->preProcess_rules($this->packets_response[self::PACKET_RULES]);
$buf = new GameQ_Buffer($data);
// Make sure the data is formatted properly
if(($header = $buf->read(5)) != "\xFF\xFF\xFF\xFF\x45")
{
throw new GameQ_ProtocolsException("Data for ".__METHOD__." does not have the proper header (should be 0xFF0xFF0xFF0xFF0x45). Header: ".bin2hex($header));
return array();
}
// Count the number of rules
$num_rules = $buf->readInt16Signed();
// Add the count of the number of rules this server has
$result->add('num_rules', $num_rules);
// Rules
while ($buf->getLength())
{
$result->add($buf->readString(), $buf->readString());
}
unset($buf);
return $result->fetch();
}
/**
* Process the packets to make sure we combine and decompress as needed
*
* @param array $packets
* @throws GameQ_ProtocolsException
* @return string
*/
protected function process_packets($packets)
{
// Make a buffer to see if we should have multiple packets
$buffer = new GameQ_Buffer($packets[0]);
// First we need to see if the packet is split
// -2 = split packets
// -1 = single packet
$packet_type = $buffer->readInt32Signed();
// This is one packet so just return the rest of the buffer
if($packet_type == -1)
{
// Free some memory
unset($buffer);
// We always return the packet as expected, with null included
return $packets[0];
}
// Free some memory
unset($buffer);
// Init array so we can order
$packs = array();
// We have multiple packets so we need to get them and order them
foreach($packets AS $packet)
{
// Make a buffer so we can read this info
$buffer = new GameQ_Buffer($packet);
// Pull some info
$packet_type = $buffer->readInt32Signed();
$request_id = $buffer->readInt32Signed();
// Check to see if this is compressed
if($request_id & 0x80000000)
{
// Check to see if we have Bzip2 installed
if(!function_exists('bzdecompress'))
{
throw new GameQ_ProtocolsException('Bzip2 is not installed. See http://www.php.net/manual/en/book.bzip2.php for more info.', 0);
return FALSE;
}
// Get some info
$num_packets = $buffer->readInt8();
$cur_packet = $buffer->readInt8();
$packet_length = $buffer->readInt32();
$packet_checksum = $buffer->readInt32();
// Try to decompress
$result = bzdecompress($buffer->getBuffer());
// Now verify the length
if(strlen($result) != $packet_length)
{
throw new GameQ_ProtocolsException("Checksum for compressed packet failed! Length expected {$packet_length}, length returned".strlen($result));
}
// Set the new packs
$packs[$cur_packet] = $result;
}
else // Normal packet
{
// Gold source does things a bit different
if($this->source_engine == self::GOLDSOURCE_ENGINE)
{
$packet_number = $buffer->readInt8();
}
else // New source
{
$packet_number = $buffer->readInt16Signed();
$split_length = $buffer->readInt16Signed();
}
// Now add the rest of the packet to the new array with the packet_number as the id so we can order it
$packs[$packet_number] = $buffer->getBuffer();
}
unset($buffer);
}
// Free some memory
unset($packets, $packet);
// Sort the packets by packet number
ksort($packs);
// Now combine the packs into one and return
return implode("", $packs);
}
}

View File

@ -0,0 +1,30 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Stalker Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Stalker extends GameQ_Protocols_Gamespy2
{
protected $name = "stalker";
protected $name_long = "S.T.A.L.K.E.R: Shadow of Chernobyl";
protected $port = 5445;
}

View File

@ -0,0 +1,338 @@
<?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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Teamspeak 2 Protocol Class
*
* This class provides some functionality for getting status information for Teamspeak 2
* servers.
*
* This code ported from GameQ v1. Credit to original author(s) as I just updated it to
* work within this new system.
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class GameQ_Protocols_Teamspeak2 extends GameQ_Protocols
{
/**
* Normalization for this protocol class
*
* @var array
*/
protected $normalize = array(
// General
'general' => array(
'dedicated' => array('dedicated'),
'hostname' => array('servername'),
'password' => array('serverpassword'),
'numplayers' => array('servercurrentusers'),
'maxplayers' => array('servermaxusers'),
'players' => array('players'),
'teams' => array('teams'),
),
// Player
'player' => array(
'id' => array('pid'),
'team' => array('cid'),
),
// Team
'team' => array(
'id' => array('id'),
),
);
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @var array
*/
protected $packets = array(
self::PACKET_DETAILS => "sel %d\x0Asi\x0A",
self::PACKET_PLAYERS => "sel %d\x0Apl\x0A",
self::PACKET_CHANNELS => "sel %d\x0Acl\x0A",
);
/**
* Methods to be run when processing the response(s)
*
* @var array
*/
protected $process_methods = array(
"process_details",
"process_channels",
"process_players",
);
/**
* Default port for this server type
*
* @var int
*/
protected $port = 8767; // Default port, used if not set when instanced
/**
* Because Teamspeak is run like a master server we have to know what port we are really querying
*
* @var int
*/
protected $master_server_port = 51234;
/**
* We have to use TCP connection
*
* @var string
*/
protected $transport = self::TRANSPORT_TCP;
/**
* The protocol being used
*
* @var string
*/
protected $protocol = 'teamspeak2';
/**
* String name of this protocol class
*
* @var string
*/
protected $name = 'teamspeak2';
/**
* Longer string name of this protocol class
*
* @var string
*/
protected $name_long = "Teamspeak 2";
/**
* We need to affect the packets we are sending before they are sent
*
* @see GameQ_Protocols_Core::beforeSend()
*/
public function beforeSend()
{
// Let's loop the packets and set the proper pieces
foreach($this->packets AS $packet_type => $packet)
{
// Update the query port for the server
$this->packets[$packet_type] = sprintf($packet, $this->port);
}
// Set the port we are connecting to the master port
$this->port = $this->master_server_port;
return TRUE;
}
/*
* Internal methods
*/
protected function preProcess($packets=array())
{
// Create a buffer
$buffer = new GameQ_Buffer(implode("", $packets));
// Verify the header
$this->verify_header($buffer);
return $buffer;
}
/**
* Process the server information
*/
protected function process_details()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_DETAILS))
{
return array();
}
// Let's preprocess the status
$buffer = $this->preProcess($this->packets_response[self::PACKET_DETAILS]);
// Set the result to a new result instance
$result = new GameQ_Result();
// Always dedicated
$result->add('dedicated', TRUE);
// Let's loop until we run out of data
while($buffer->getLength())
{
// Grab the row, which is an item
// Check for end of packet
if(($row = trim($buffer->readString("\n"))) == 'OK')
{
break;
}
// Split out the information
list($key, $value) = explode('=', $row, 2);
// Add this to the result
$result->add($key, $value);
}
unset($buffer, $row, $key, $value);
return $result->fetch();
}
/**
* Process the channel listing
*/
protected function process_channels()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_CHANNELS))
{
return array();
}
// Let's preprocess the status
$buffer = $this->preProcess($this->packets_response[self::PACKET_CHANNELS]);
// Set the result to a new result instance
$result = new GameQ_Result();
// The first line holds the column names, data returned is in column/row format
$columns = explode("\t", trim($buffer->readString("\n")), 9);
// Loop thru the rows until we run out of information
while($buffer->getLength())
{
// Grab the row, which is a tabbed list of items
// Check for end of packet
if(($row = trim($buffer->readString("\n"))) == 'OK')
{
break;
}
// Explode and merge the data with the columns, then parse
$data = array_combine($columns, explode("\t", $row, 9));
foreach($data AS $key => $value)
{
// Now add the data to the result
$result->addTeam($key, $value);
}
}
unset($data, $buffer, $row, $columns, $key, $value);
return $result->fetch();
}
/**
* Process the players response
*/
protected function process_players()
{
// Make sure we have a valid response
if(!$this->hasValidResponse(self::PACKET_PLAYERS))
{
return array();
}
// Let's preprocess the status
$buffer = $this->preProcess($this->packets_response[self::PACKET_PLAYERS]);
// Set the result to a new result instance
$result = new GameQ_Result();
// The first line holds the column names, data returned is in column/row format
$columns = explode("\t", trim($buffer->readString("\n")), 16);
// Loop thru the rows until we run out of information
while($buffer->getLength())
{
// Grab the row, which is a tabbed list of items
// Check for end of packet
if(($row = trim($buffer->readString("\n"))) == 'OK')
{
break;
}
// Explode and merge the data with the columns, then parse
$data = array_combine($columns, explode("\t", $row, 16));
foreach($data AS $key => $value)
{
// Now add the data to the result
$result->addPlayer($key, $value);
}
}
unset($data, $buffer, $row, $columns, $key, $value);
return $result->fetch();
}
/**
* Verify the header of the returned response packet
*
* @param GameQ_Buffer $buffer
* @throws GameQ_ProtocolsException
*/
protected function verify_header(GameQ_Buffer &$buffer)
{
// Check length
if($buffer->getLength() < 6)
{
throw new GameQ_ProtocolsException(__METHOD__.": Length of buffer is not long enough");
return FALSE;
}
// Check to make sure the header is correct
if(($type = trim($buffer->readString("\n"))) != '[TS]')
{
throw new GameQ_ProtocolsException(__METHOD__.": Header returned did not match. Returned type {$type}");
return FALSE;
}
// Verify the response and return
return $this->verify_response(trim($buffer->readString("\n")));
}
/**
* Verify the response for the specific entity
*
* @param string $response
* @throws GameQ_ProtocolsException
*/
protected function verify_response($response)
{
// Check the response
if($response != 'OK')
{
throw new GameQ_ProtocolsException(__METHOD__.": Header return response was no 'OK'. Returned response {$response}");
return FALSE;
}
return TRUE;
}
}

Some files were not shown because too many files have changed in this diff Show More