Panel/ControlPanel/protocol/GameQ/gameq/protocols/core.php

709 lines
14 KiB
PHP
Executable file

<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
/**
* 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 port the client can connect on, usually the same as self::$port
* but not always.
*
* @var integer
*/
protected $port_client = 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;
/**
* Quick join link for specific games
*
* @var string
*/
protected $join_link = NULL;
/**
* Create the instance.
*
* @param string $ip
* @param mixed $port false|int
* @param array $options
*/
public function __construct($ip = FALSE, $port = FALSE, $options = array())
{
// Set the ip
$this->ip($ip);
// We have a specific port set so let's set it.
if($port !== FALSE)
{
// Set the port
$this->port($port);
/*
* By default we set the client port = to the query port. Note that
* this is not always the case
*/
$this->port_client($port);
}
// We have passed options so let's set them
if(!empty($options))
{
// Set the passed options
$this->options($options);
// We have an option passed for client connect port
if(isset($options['client_connect_port']) && !empty($options['client_connect_port']))
{
// Overwrite the default connect port
$this->port_client($options['client_connect_port']);
}
}
}
/**
* 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 client port of the server
*
* @param integer $port
*/
public function port_client($port = FALSE)
{
// Act as setter
if($port !== FALSE)
{
$this->port_client = $port;
}
return $this->port_client;
}
/**
* 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_name'] = $this->name_long();
$results['gq_transport'] = $this->transport;
// Process the join link
if(!isset($results['gq_joinlink']) || empty($results['gq_joinlink']))
{
$results['gq_joinlink'] = $this->getJoinLink();
}
// 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;
}
/**
* Create a server join link based on the server information
*
* @return string
*/
protected function getJoinLink()
{
$link = '';
// We have a join_link defined
if(!empty($this->join_link))
{
$link = sprintf($this->join_link, $this->ip, $this->port_client);
}
return $link;
}
}