Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save DavidGannon/6926820 to your computer and use it in GitHub Desktop.
Save DavidGannon/6926820 to your computer and use it in GitHub Desktop.
<div class="wrap">
<h4>SRCD Server Info</h4>
<table class="widefat">
<thead>
<tr>
<td>
Server IP:
</td>
</tr>
</thead>
<tfoot>
<tr>
<td>Server Ip</td>
</tr>
</tfoot>
</table>
</div>
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2012, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'exceptions/BufferUnderflowException.php';
/**
* This class represents a byte buffer which helps reading byte-wise data from
* a string which acts as a raw byte array.
*
* @author Sebastian Staudt
* @package steam-condenser
*/
class ByteBuffer {
/**
* @var string The string holding the buffer's bytes
*/
private $byteArray;
/**
* @var int The size of the buffer
*/
private $capacity;
/**
* @var int The limit to which the buffer may be filled
*/
private $limit;
/**
* @var int The current position of the read pointer
*/
private $position;
/**
* Allocates a string with the specified amount of bytes wrapped into a
* byte buffer object
*
* @param int $length The size of the byte buffer
* @return ByteBuffer The new byte buffer object
*/
public static function allocate($length) {
return new ByteBuffer(str_repeat("\0", $length));
}
/**
* Wraps an existing string into a byte buffer object
*
* @param string $byteArray The string to encapsulate into the byte buffer
* @return ByteBuffer The new ByteBuffer object
*/
public static function wrap($byteArray) {
return new ByteBuffer($byteArray);
}
/**
* Creates a new byte buffer instance
*
* @param string $byteArray The string to encapsulate into the
* byte buffer
*/
public function __construct($byteArray) {
$this->byteArray = $byteArray;
$this->capacity = strlen($byteArray);
$this->limit = $this->capacity;
$this->position = 0;
}
/**
* Returns the string wrapped into this byte buffer object
*
* @return string The string encapsulated in this byte buffer
*/
public function _array() {
return $this->byteArray;
}
/**
* Clears the state of this byte buffer object
*
* Sets the <var>limit</var> to the <var>capacity</var> of the buffer and
* resets the <var>position</var>.
*/
public function clear() {
$this->limit = $this->capacity;
$this->position = 0;
}
/**
* Sets the <var>limit</var> to the current <var>position</var> before
* resetting the <var>position</var>.
*
* @return ByteBuffer This byte buffer
*/
public function flip() {
$this->limit = $this->position;
$this->position = 0;
return $this;
}
/**
* Reads the specified amount of bytes from the current <var>position</var>
* of the byte buffer
*
* @param int $length The amount of bytes to read from the buffer or
* <var>null</var> if everything up to <var>limit</var> should be
* read
* @return string The data read from the buffer
* @throws BufferUnderflowException if the buffer does not contain $length
* or more bytes after the current position
*/
public function get($length = null) {
if($length === null) {
$length = $this->limit - $this->position;
} elseif($length > $this->remaining()) {
throw new BufferUnderFlowException();
}
$data = substr($this->byteArray, $this->position, $length);
$this->position += $length;
return $data;
}
/**
* Reads a single byte from the buffer
*
* @return int The byte at the current position
*/
public function getByte() {
return ord($this->get(1));
}
/**
* Reads a floating point number from the buffer
*
* @return float The floating point number, i.e. four bytes converted to a
* <var>float</var> read at the current position
*/
public function getFloat() {
$data = unpack('f', $this->get(4));
return $data[1];
}
/**
* Reads a long integer from the buffer
*
* @return int The long integer, i.e. four bytes converted to an
* <var>int</var> read at the current position
*/
public function getLong() {
$data = unpack('l', $this->get(4));
return $data[1];
}
/**
* Reads a short integer from the buffer
*
* @return int The short integer, i.e. two bytes converted to an
* <var>int</var> read at the current position
*/
public function getShort() {
$data = unpack('v', $this->get(2));
return $data[1];
}
/**
* Reads a zero-byte terminated string from the current position of the
* byte buffer
*
* This reads the buffer up until the first occurance of a zero-byte or the
* end of the buffer. The zero-byte is not included in the returned string.
*
* @return string The zero-byte terminated string read from the byte stream
*/
public function getString() {
$zeroByteIndex = strpos($this->byteArray, "\0", $this->position);
if($zeroByteIndex === false) {
return '';
} else {
$dataString = $this->get($zeroByteIndex - $this->position);
$this->position ++;
return $dataString;
}
}
/**
* Reads an unsigned long integer from the buffer
*
* @return int The long integer, i.e. four bytes converted to an unsigned
* <var>float</var> read at the current position
*/
public function getUnsignedLong() {
$data = unpack('V', $this->get(4));
return $data[1];
}
/**
* Sets or returns the <var>limit</var> of the buffer
*
* @param int $newLimit Sets the buffer's <var>limit</var> to this value
* @return int If no new <var>limit</var> value is given, the current value
*/
public function limit($newLimit = null) {
if($newLimit == null) {
return $this->limit;
} else {
$this->limit = $newLimit;
return null;
}
}
/**
* Returns the current <var>position</var> of the buffer
*
* @return int The current <var>position</var> of the buffer
*/
public function position() {
return $this->position;
}
/**
* Replaces the contents of the byte buffer with the bytes from the source
* string beginning at the current <var>position</var>
*
* @param string $sourceByteArray The string to take bytes from
* @return ByteBuffer This byte buffer
*/
public function put($sourceByteArray) {
$newPosition = min($this->remaining(), strlen($sourceByteArray));
$this->byteArray = substr_replace($this->byteArray, $sourceByteArray, $this->position, $newPosition);
$this->position = $newPosition;
return $this;
}
/**
* Returns the remaining number of byte from the current
* <var>position</var> to the <var>limit</var> of the buffer
*
* @return int The number of bytes remaining in the buffer
*/
public function remaining() {
return $this->limit - $this->position;
}
/**
* Resets the <var>position</var> of this buffer
*
* @return ByteBuffer This byte buffer
*/
public function rewind() {
$this->position = 0;
return $this;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
/**
* This exception class indicates a problem while reading from a
* <var>ByteBuffer</var> instance, i.e. reading more data than the buffer
* contains. This may hint at a protocol related problem.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage exceptions
* @see ByteBuffer
*/
class BufferUnderflowException extends Exception {}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'exceptions/SteamCondenserException.php';
/**
* This exception class indicates a problem when parsing packet data from the
* responses received from a game or master server
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage exceptions
*/
class PacketFormatException extends SteamCondenserException {
/**
* Creates a new <var>PacketFormatException</var> instance
*
* @param string $message The message to attach to the exception
*/
public function __construct($message = "The packet data received doesn't match the packet format.") {
parent::__construct($message);
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2009-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'exceptions/SteamCondenserException.php';
/**
* This exception class indicates that the IP address your accessing the game
* server from has been banned by the server
*
* You or the server operator will have to unban your IP address on the server.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage exceptions
* @see GameServer::rconAuth()
*/
class RCONBanException extends SteamCondenserException {
/**
* Creates a new <var>RCONBanException</var> instance
*/
public function __construct() {
parent::__construct('You have been banned from this server.');
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'exceptions/SteamCondenserException.php';
/**
* This exception class indicates that you have not authenticated yet with the
* game server you're trying to send commands via RCON
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage exceptions
* @see GameServer::rconAuth()
* @see GameServer::rconExec()
*/
class RCONNoAuthException extends SteamCondenserException {
/**
* Creates a new <var>RCONNoAuthException</var> instance
*/
public function __construct() {
parent::__construct('Not authenticated yet.');
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2013, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'exceptions/SteamCondenserException.php';
/**
* This exception class is used to indicate errors while commmunicating through
* UDP or TCP sockets
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage exceptions
*/
class SocketException extends SteamCondenserException {
/**
* @var int
*/
protected $errorCode;
/**
* Create a new instance with an error code provided by the sockets
* extension or with the given message.
*
* @param int|string $errorCode The error code or message
* @see socket_lasterror()
* @see socket_strerror()
*/
public function __construct($errorCode) {
if (is_string($errorCode)) {
$errorMessage = $errorCode;
$errorCode = null;
} else {
$this->errorCode = $errorCode;
$errorMessage = socket_strerror($errorCode) . " (Code: $errorCode)";
}
parent::__construct($errorMessage, $errorCode);
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
/**
* This exception class is used as a base class for all exceptions related to
* Steam Condenser's operation
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage exceptions
*/
class SteamCondenserException extends Exception {}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
/**
* This exception class indicates that an operation could not be finished
* within a reasonable amount of time
*
* This usually indicates that a server could not be contacted because of
* network problems.
*
* <b>Note:</b> {@link SteamSocket::setTimeout()} allows to set a custom
* timeout for socket operations
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage exceptions
*/
class TimeoutException extends Exception {
/**
* Creates a new <var>TimeoutException</var> instance
*/
public function __construct() {
parent::__construct('The operation timed out.');
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2010-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'exceptions/SteamCondenserException.php';
/**
* This exception is raised when a Steam Web API request or a related action
* fails. This can have various reasons like an invalid Web API key or a broken
* request.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage exceptions
* @see WebApi
*/
class WebApiException extends SteamCondenserException {
const HTTP_ERROR = 0;
const INVALID_KEY = 1;
const STATUS_BAD = 2;
const UNAUTHORIZED = 3;
/**
* Creates a new WebApiException with an error message according to the
* given <var>$cause</var>. If this cause is <var>STATUS_BAD</var> (which
* will origin from the Web API itself) or <var>HTTP_ERROR</var> the
* details about this failed request will be taken from
* <var>$statusCode</var> and <var>$statusMessage</var>.
*
* @param int $cause An integer indicating the problem which caused this
* exception:
*
* <ul>
* <li><var>HTTP_ERROR</var>: An error during the HTTP request
* itself will result in an exception with this reason.</li>
* <li><var>INVALID_KEY</var>: This occurs when trying to set a Web
* API key that isn't valid, i.e. a 128 bit integer in a
* hexadecimal string.
* <li><var>STATUS_BAD</var>: This is caused by a successful request
* that fails for some Web API internal reason (e.g. an invalid
* argument). Details about this failed request will be taken
* from <var>$statusCode</var> and <var>$statusMessage</var>.
* <li><var>UNAUTHORIZED</var>: This happens when a Steam Web API
* request is rejected as unauthorized. This most likely means
* that you did not specify a valid Web API key using
* {@link WebApi::setApiKey()}. A Web API key can be obtained
* from http://steamcommunity.com/dev/apikey.
* </ul>
*
* Other undefined reasons will cause a generic error message.
* @param int $statusCode The HTTP status code returned by the Web API
* @param string $statusMessage The status message returned in the response
*/
public function __construct($cause, $statusCode = null, $statusMessage = '') {
switch($cause) {
case self::HTTP_ERROR:
$message = "The Web API request has failed due to an HTTP error: $statusMessage (status code: $statusCode).";
break;
case self::INVALID_KEY:
$message = 'This is not a valid Steam Web API key.';
break;
case self::STATUS_BAD:
$message = "The Web API request failed with the following error: $statusMessage (status code: $statusCode).";
break;
case self::UNAUTHORIZED:
$message = 'Your Web API request has been rejected. You most likely did not specify a valid Web API key.';
break;
default:
$message = 'An unexpected error occurred while executing a Web API request.';
}
parent::__construct($message);
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2013, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'exceptions/SocketException.php';
/**
* This class represents an IP socket
*
* It can connect to a remote host, send and receive packets
*
* @author Sebastian Staudt
* @package steam-condenser
*/
abstract class Socket {
/**
* The IP address the socket is connected to
*
* @var string
*/
protected $ipAddress;
/**
* The port number the socket is connected to
*
* @var int
*/
protected $portNumber;
/**
* @var string
*/
protected $readBuffer = '';
/**
* The socket itself
* @var resource
*/
protected $socket;
/**
* Stores if the sockets extension is loaded
* @var bool
*/
protected $socketsEnabled;
/**
* Constructs the Socket object
*
* This will check if PHP's sockets extension is loaded which might be used
* for socket communication.
*/
public function __construct() {
$this->socketsEnabled = extension_loaded('sockets');
}
/**
* Destructor of this socket
*
* Automatically calls close()
*/
public function __destruct() {
$this->close();
}
/**
* Connects the socket to the host with the given IP address and port
* number
*
* @param string $ipAddress The IP address to connect to
* @param int $portNumber The TCP port to connect to
* @param int $timeout The timeout in milliseconds
*/
abstract public function connect($ipAddress, $portNumber, $timeout);
/**
* Closes the socket
*/
public function close() {
if(!empty($this->socket)) {
if($this->socketsEnabled) {
@socket_close($this->socket);
} else {
@fclose($this->socket);
}
$this->socket = null;
}
}
/**
* Returns whether this socket has an open connection
*
* @return bool <var>true</var> if this socket is open
*/
public function isOpen() {
return !empty($this->socket);
}
/**
* Receives the specified amount of data from the socket
*
* @param int $length The number of bytes to read from the socket
* @return string The data read from the socket
* @throws SocketException if reading from the socket fails
*/
public function recv($length = 128) {
if($this->socketsEnabled) {
$data = @socket_read($this->socket, $length, PHP_BINARY_READ);
if ($data === false) {
throw new SocketException(socket_last_error($this->socket));
}
} else {
$data = fread($this->socket, $length);
if ($data === false) {
throw new SocketException('Could not read from socket.');
}
}
return $data;
}
/**
* Waits for data to be read from this socket before the specified timeout
* occurs
*
* @param int $timeout The number of milliseconds to wait for data arriving
* on this socket before timing out
* @return bool whether data arrived on this socket before the timeout
*/
public function select($timeout = 0) {
$read = array($this->socket);
$write = null;
$except = null;
$sec = floor($timeout / 1000);
$usec = $timeout % 1000;
if($this->socketsEnabled) {
$select = socket_select($read, $write, $except, $sec, $usec);
} else {
$select = stream_select($read, $write, $except, $sec, $usec);
}
return $select > 0;
}
/**
* Sends the specified data to the peer this socket is connected to
*
* @param string $data The data to send to the connected peer
* @throws SocketException if sending fails
*/
public function send($data) {
if($this->socketsEnabled) {
$sendResult = socket_send($this->socket, $data, strlen($data), 0);
if ($sendResult === false) {
throw new SocketException(socket_last_error($this->socket));
}
} else {
$sendResult = fwrite($this->socket, $data, strlen($data));
if ($sendResult === false) {
throw new SocketException('Could not send data.');
}
}
}
/**
* Returns the file descriptor of the underlying socket
*
* @return resource The underlying socket descriptor
*/
public function resource() {
return $this->socket;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2010-2013, Sebastian Staudt
*
* @author Sebastian Staudt
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
* @package steam-condenser
*/
define('STEAM_CONDENSER_PATH', dirname(__FILE__) . '/');
define('STEAM_CONDENSER_VERSION', '1.3.6');
require_once STEAM_CONDENSER_PATH . 'steam/servers/GoldSrcServer.php';
require_once STEAM_CONDENSER_PATH . 'steam/servers/MasterServer.php';
require_once STEAM_CONDENSER_PATH . 'steam/servers/SourceServer.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/SteamId.php';
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2010-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
/**
* This class holds statistical information about missions played by a player
* in Alien Swarm
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class AlienSwarmMission {
/**
* @var float
*/
private $avgDamageTaken;
/**
* @var float
*/
private $avgFriendlyFire;
/**
* @var float
*/
private $avgKills;
/**
* @var string
*/
private $bestDifficulty;
/**
* @var int
*/
private $damageTaken;
/**
* @var int
*/
private $friendlyFire;
/**
* @var int
*/
private $gamesSuccessful;
/**
* @var string
*/
private $img;
/**
* @var int
*/
private $kills;
/**
* @var string
*/
private $mapName;
/**
* @var string
*/
private $name;
/**
* @var int
*/
private $time;
/**
* @var string
*/
private $totalGames;
/**
* @var float
*/
private $totalGamesPercentage;
/**
* Creates a new mission instance of based on the given XML data
*
* @param SimpleXMLElement $missionData The data representing this mission
*/
public function __construct(SimpleXMLElement $missionData) {
$this->avgDamageTaken = (float) $missionData->damagetakenavg;
$this->avgFriendlyFire = (float) $missionData->friendlyfireavg;
$this->avgKills = (float) $missionData->killsavg;
$this->bestDifficulty = (string) $missionData->bestdifficulty;
$this->damageTaken = (int) $missionData->damagetaken;
$this->friendlyFire = (int) $missionData->friendlyfire;
$this->gamesSuccessful = (int) $missionData->gamessuccess;
$this->img = AlienSwarmStats::BASE_URL . (string) $missionData->image;
$this->kills = (int) $missionData->kills;
$this->mapName = $missionData->getName();
$this->name = (string) $missionData->name;
$this->totalGames = (int) $missionData->gamestotal;
$this->totalGamesPercentage = (float) $missionData->gamestotalpct;
$this->time = array();
$this->time['average'] = (string) $missionData->avgtime;
$this->time['brutal'] = (string) $missionData->brutaltime;
$this->time['easy'] = (string) $missionData->easytime;
$this->time['hard'] = (string) $missionData->hardtime;
$this->time['insane'] = (string) $missionData->insanetime;
$this->time['normal'] = (string) $missionData->normaltime;
$this->time['total'] = (string) $missionData->totaltime;
}
/**
* Returns the avarage damage taken by the player while playing a round in
* this mission
*
* @return float The average damage taken by the player
*/
public function getAvgDamageTaken() {
return $this->avgDamageTaken;
}
/**
* Returns the avarage damage dealt by the player to team mates while
* playing a round in this mission
*
* @return float The average damage dealt by the player to team mates
*/
public function getAvgFriendlyFire() {
return $this->avgFriendlyFire;
}
/**
* Returns the avarage number of aliens killed by the player while playing
* a round in this mission
*
* @return float The avarage number of aliens killed by the player
*/
public function getAvgKills() {
return $this->avgKills;
}
/**
* Returns the highest difficulty the player has beat this mission in
*
* @return string The highest difficulty the player has beat this mission
* in
*/
public function getBestDifficulty() {
return $this->bestDifficulty;
}
/**
* Returns the total damage taken by the player in this mission
*
* @return int The total damage taken by the player
*/
public function getDamageTaken() {
return $this->damageTaken;
}
/**
* Returns the total damage dealt by the player to team mates in this
* mission
*
* @return int The total damage dealt by the player to team mates
*/
public function getFriendlyFire() {
return $this->friendlyFire;
}
/**
* Returns the number of successful rounds the player played in this
* mission
*
* @return int The number of successful rounds of this mission
*/
public function getGamesSuccessful() {
return $this->gamesSuccessful;
}
/**
* Returns the URL to a image displaying the mission
*
* @return string The URL of the mission's image
*/
public function getImg() {
return $this->img;
}
/**
* Returns the total number of aliens killed by the player in this mission
*
* @return int The total number of aliens killed by the player
*/
public function getKills() {
return $this->kills;
}
/**
* Returns the file name of the mission's map
*
* @return string The file name of the mission's map
*/
public function getMapName() {
return $this->mapName;
}
/**
* Returns the name of the mission
*
* @return string The name of the mission
*/
public function getName() {
return $this->name;
}
/**
* Returns various statistics about the times needed to accomplish this
* mission
*
* This includes the best times for each difficulty, the average time and
* the total time spent in this mission.
*
* @return array Various time statistics about this mission
*/
public function getTime() {
return $this->time;
}
/**
* Returns the number of games played in this mission
*
* @return int The number of games played in this mission
*/
public function getTotalGames() {
return $this->totalGames;
}
/**
* Returns the percentage of successful games played in this mission
*
* @return float The percentage of successful games played in this mission
*/
public function getTotalGamesPercentage() {
return $this->totalGamesPercentage;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2010-2012, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/alien_swarm/AlienSwarmMission.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/alien_swarm/AlienSwarmWeapon.php';
/**
* This class represents the game statistics for a single user in Alien Swarm
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class AlienSwarmStats extends GameStats {
/**
* @var string The base URL for all images referenced in the stats
*/
const BASE_URL = 'http://steamcommunity.com/public/images/gamestats/swarm/';
/**
* @var string The names of all weapons in Alien Swarm
*/
private static $WEAPONS = array('Autogun', 'Cannon_Sentry', 'Chainsaw',
'Flamer', 'Grenade_Launcher', 'Hand_Grenades', 'Hornet_Barrage',
'Incendiary_Sentry', 'Laser_Mines', 'Marskman_Rifle', 'Minigun',
'Mining_Laser', 'PDW', 'Pistol', 'Prototype_Rifle', 'Rail_Rifle',
'Rifle', 'Rifle_Grenade', 'Sentry_Gun', 'Shotgun', 'Tesla_Cannon',
'Vindicator', 'Vindicator_Grenade');
/**
* @var array
*/
private $favorites;
/**
* @var array
*/
private $itemStats;
/**
* @var array
*/
private $lifetimeStats;
/**
* @var array
*/
private $missionStats;
/**
* @var array
*/
private $weaponStats;
/**
* Creates a new <var>AlienSwarmStats</var> instance by calling the super
* constructor with the game name <var>"alienswarm"</var>
*
* @param mixed $steamId The custom URL or the 64bit Steam ID of the user
*/
public function __construct($steamId) {
parent::__construct($steamId, 'alienswarm');
if($this->isPublic()) {
$lifetimeStats = $this->xmlData->stats->lifetime;
$this->hoursPlayed = (string) $lifetimeStats->timePlayed;
$this->lifetimeStats = array();
$this->lifetimeStats['accuracy'] = (float) $lifetimeStats->accuracy;
$this->lifetimeStats['aliensBurned'] = (int) $lifetimeStats->aliensburned;
$this->lifetimeStats['aliensKilled'] = (int) $lifetimeStats->alienskilled;
$this->lifetimeStats['campaigns'] = (int) $lifetimeStats->campaigns;
$this->lifetimeStats['damageTaken'] = (int) $lifetimeStats->damagetaken;
$this->lifetimeStats['experience'] = (int) $lifetimeStats->experience;
$this->lifetimeStats['experienceRequired'] = (int) $lifetimeStats->xprequired;
$this->lifetimeStats['fastHacks'] = (int) $lifetimeStats->fasthacks;
$this->lifetimeStats['friendlyFire'] = (int) $lifetimeStats->friendlyfire;
$this->lifetimeStats['gamesSuccessful'] = (int) $lifetimeStats->gamessuccess;
$this->lifetimeStats['healing'] = (int) $lifetimeStats->healing;
$this->lifetimeStats['killsPerHour'] = (float) $lifetimeStats->killsperhour;
$this->lifetimeStats['level'] = (int) $lifetimeStats->level;
$this->lifetimeStats['promotion'] = (int) $lifetimeStats->promotion;
$this->lifetimeStats['nextUnlock'] = (string) $lifetimeStats->nextunlock;
$this->lifetimeStats['nextUnlockImg'] = self::BASE_URL . (string) $lifetimeStats->nextunlockimg;
$this->lifetimeStats['shotsFired'] = (int) $lifetimeStats->shotsfired;
$this->lifetimeStats['totalGames'] = (int) $lifetimeStats->totalgames;
if($this->lifetimeStats['promotion'] > 0) {
$this->lifetimeStats['promotionImg'] = self::BASE_URL . (string) $lifetimeStats->promotionpic;
}
$this->lifetimeStats['games_successful_percentage'] = ($this->lifetimeStats['totalGames'] > 0) ? $this->lifetimeStats['gamesSuccessful'] / $this->lifetimeStats['totalGames'] : 0;
}
}
/**
* Returns the favorites of this user like weapons and marine
*
* If the favorites haven't been parsed already, parsing is done now.
*
* @return array The favorites of this player
*/
public function getFavorites() {
if(!$this->isPublic()) {
return null;
}
if(empty($this->favorites)) {
$favoritesData = $this->xmlData->stats->favorites;
$this->favorites = array();
$this->favorites['class'] = (string) $favoritesData->class;
$this->favorites['classImg'] = (string) $favoritesData->classimg;
$this->favorites['classPercentage'] = (float) $favoritesData->classpct;
$this->favorites['difficulty'] = (string) $favoritesData->difficulty;
$this->favorites['difficultyPercentage'] = (float) $favoritesData->difficultypct;
$this->favorites['extra'] = (string) $favoritesData->extra;
$this->favorites['extraImg'] = (string) $favoritesData->extraimg;
$this->favorites['extraPercentage'] = (float) $favoritesData->extrapct;
$this->favorites['marine'] = (string) $favoritesData->marine;
$this->favorites['marineImg'] = (string) $favoritesData->marineimg;
$this->favorites['marinePercentage'] = (float) $favoritesData->marinepct;
$this->favorites['mission'] = (string) $favoritesData->mission;
$this->favorites['missionImg'] = (string) $favoritesData->missionimg;
$this->favorites['missionPercentage'] = (float) $favoritesData->missionpct;
$this->favorites['primaryWeapon'] = (string) $favoritesData->primary;
$this->favorites['primaryWeaponImg'] = (string) $favoritesData->primaryimg;
$this->favorites['primaryWeaponPercentage'] = (float) $favoritesData->primarypct;
$this->favorites['secondaryWeapon'] = (string) $favoritesData->secondary;
$this->favorites['secondaryWeaponImg'] = (string) $favoritesData->secondaryimg;
$this->favorites['secondaryWeapon_Percentage'] = (float) $favoritesData->secondarypct;
}
return $this->favorites;
}
/**
* Returns the item stats for this user like ammo deployed and medkits
* used
*
* If the items haven't been parsed already, parsing is done now.
*
* @return array The item stats of this player
*/
public function getItemStats() {
if(!$this->isPublic()) {
return null;
}
if(empty($this->itemStats)) {
$itemStatsData = $this->xmlData->stats->weapons;
$this->itemStats = array();
$this->itemStats['ammoDeployed'] = (int) $itemStatsData->ammo_deployed;
$this->itemStats['sentrygunsDeployed'] = (int) $itemStatsData->sentryguns_deployed;
$this->itemStats['sentryFlamersDeployed'] = (int) $itemStatsData->sentry_flamers_deployed;
$this->itemStats['sentryFreezeDeployed'] = (int) $itemStatsData->sentry_freeze_deployed;
$this->itemStats['sentryCannonDeployed'] = (int) $itemStatsData->sentry_cannon_deployed;
$this->itemStats['medkitsUsed'] = (int) $itemStatsData->medkits_used;
$this->itemStats['flaresUsed'] = (int) $itemStatsData->flares_used;
$this->itemStats['adrenalineUsed'] = (int) $itemStatsData->adrenaline_used;
$this->itemStats['teslaTrapsDeployed'] = (int) $itemStatsData->tesla_traps_deployed;
$this->itemStats['freezeGrenadesThrown'] = (int) $itemStatsData->freeze_grenades_thrown;
$this->itemStats['electricArmorUsed'] = (int) $itemStatsData->electric_armor_used;
$this->itemStats['healgunHeals'] = (int) $itemStatsData->healgun_heals;
$this->itemStats['healgunHealsSelf'] = (int) $itemStatsData->healgun_heals_self;
$this->itemStats['healbeaconHeals'] = (int) $itemStatsData->healbeacon_heals;
$this->itemStats['healbeaconHealsSelf'] = (int) $itemStatsData->healbeacon_heals_self;
$this->itemStats['damageAmpsUsed'] = (int) $itemStatsData->damage_amps_used;
$this->itemStats['healbeaconsDeployed'] = (int) $itemStatsData->healbeacons_deployed;
$this->itemStats['healbeaconHealsPct'] = (float) $itemStatsData->healbeacon_heals_pct;
$this->itemStats['healgunHealsPct'] = (float) $itemStatsData->healgun_heals_pct;
$this->itemStats['healbeaconHealsPctSelf'] = (float) $itemStatsData->healbeacon_heals_pct_self;
$this->itemStats['healgunHealsPctSelf'] = (float) $itemStatsData->healgun_heals_pct_self;
}
return $this->itemStats;
}
/**
* Returns general stats for the players
*
* @return array The stats for the player
*/
public function getLifetimeStats() {
return $this->lifetimeStats;
}
/**
* Returns the stats for individual missions for this user containing all
* Alien Swarm missions
*
* If the mission stats haven't been parsed already, parsing is done now.
*
* @return array The mission stats for this player
*/
public function getMissionStats() {
if(!$this->isPublic()) {
return null;
}
if(empty($this->missionStats)) {
$this->missionStats = array();
foreach($this->xmlData->stats->missions->children() as $missionData) {
$this->missionStats[$missionData->getName()] = new AlienSwarmMission($missionData);
}
}
return $this->missionStats;
}
/**
* Returns the stats for individual weapons for this user containing all
* Alien Swarm weapons
*
* If the weapon stats haven't been parsed already, parsing is done now.
*
* @return array The weapon stats for this player
*/
public function getWeaponStats() {
if(!$this->isPublic()) {
return null;
}
if(empty($this->weaponStats)) {
$this->weaponStats = array();
foreach(self::$WEAPONS as $weaponNode) {
$weaponData = $this->xmlData->stats->weapons->$weaponNode;
$weapon = new AlienSwarmWeapon($weaponData);
$this->weaponStats[$weapon->getName()] = $weapon;
}
}
return $this->weaponStats;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2010-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/GameWeapon.php';
/**
* This class holds statistical information about weapons used by a player
* in Alien Swarm
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class AlienSwarmWeapon extends GameWeapon {
private $accuracy;
private $damage;
private $friendlyFire;
private $name;
/**
* Creates a new weapon instance based on the assigned weapon XML data
*
* @param SimpleXMLElement $weaponData The data representing this weapon
*/
public function __construct(SimpleXMLElement $weaponData) {
parent::__construct($weaponData);
$this->accuracy = (float) $weaponData->accuracy;
$this->damage = (int) $weaponData->damage;
$this->friendlyFire = (int) $weaponData->friendlyfire;
$this->name = (string) $weaponData->name;
$this->shots = (int) $weaponData->shotsfired;
}
/**
* Returns the accuracy of the player with this weapon
*
* @return The accuracy of the player with this weapon
*/
public function getAccuracy() {
return $this->accuracy;
}
/**
* Returns the damage achieved with this weapon
*
* @return The damage achieved with this weapon
*/
public function getDamage() {
return $this->damage;
}
/**
* Returns the damage dealt to team mates with this weapon
*
* @return The damage dealt to team mates with this weapon
*/
public function getFriendlyFire() {
return $this->friendlyFire;
}
/**
* Returns the name of this weapon
*
* @return The name of this weapon
*/
public function getName() {
return $this->name;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2010-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/WebApi.php';
/**
* This class represents Steam news and can be used to load a list of current
* news about specific games
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class AppNews {
/**
* @var int
*/
private $appId;
/**
* @var string
*/
private $author;
/**
* @var string
*/
private $contents;
/**
* @var int
*/
private $date;
/**
* @var bool
*/
private $external;
/**
* @var string
*/
private $feedLabel;
/**
* @var string
*/
private $feedName;
/**
* @var string
*/
private $gid;
/**
* @var string
*/
private $title;
/**
* @var string
*/
private $url;
/**
* Loads the news for the given game with the given restrictions
*
* @param int $appId The unique Steam Application ID of the game (e.g. 440
* for Team Fortress 2). See
* http://developer.valvesoftware.com/wiki/Steam_Application_IDs for
* all application IDs
* @param int $count The maximum number of news to load (default: 5).
* There's no reliable way to load all news. Use really a really
* great number instead
* @param int $maxLength The maximum content length of the news (default:
* null). If a maximum length is defined, the content of the news
* will only be at most <var>maxLength</var> characters long plus an
* ellipsis
* @return array An array of news items for the specified game with the
* given options
*/
public static function getNewsForApp($appId, $count = 5, $maxLength = null) {
$params = array('appid' => $appId, 'count' => $count,
'maxlength' => $maxLength);
$data = json_decode(WebApi::getJSON('ISteamNews', 'GetNewsForApp', 2, $params));
$newsItems = array();
foreach($data->appnews->newsitems as $newsData) {
$newsItems[] = new AppNews($appId, $newsData);
}
return $newsItems;
}
/**
* Creates a new instance of an AppNews news item with the given data
*
* @param int $appId The unique Steam Application ID of the game (e.g. 440
* for Team Fortress 2). See
* http://developer.valvesoftware.com/wiki/Steam_Application_IDs for
* all application IDs
* @param stdClass $newsData The news data extracted from JSON
*/
private function __construct($appId, $newsData) {
$this->appId = $appId;
$this->author = $newsData->author;
$this->contents = trim($newsData->contents);
$this->date = (int) $newsData->date;
$this->external = (bool) $newsData->is_external_url;
$this->feedLabel = $newsData->feedlabel;
$this->feedName = $newsData->feedname;
$this->gid = $newsData->gid;
$this->title = $newsData->title;
$this->url = $newsData->url;
}
/**
* Returns the Steam Application ID of the game this news belongs to
*
* @return int The application ID of the game this news belongs to
*/
public function getAppId() {
return $this->appId;
}
/**
* Returns the author of this news item
*
* @return string The author of this news
*/
public function getAuthor() {
return $this->author;
}
/**
* Returns the content of this news item
*
* This might contain HTML code.
*
* <strong>Note:</strong> Depending on the setting for the maximum length
* of a news (see {@link #getNewsForApp}, the contents might be truncated.
*
* @return string The content of this news
*/
public function getContents() {
return $this->contents;
}
/**
* Returns the date this news item has been published
*
* @return int The date this news has been published
*/
public function getDate() {
return $this->date;
}
/**
* Returns the name of the feed this news item belongs to
*
* @return string The name of the feed this news belongs to
*/
public function getFeedLabel() {
return $this->feedLabel;
}
/**
* Returns the symbolic name of the feed this news item belongs to
*
* @return string The symbolic name of the feed this news belongs to
*/
public function getFeedName() {
return $this->feedName;
}
/**
* Returns a unique identifier for this news
*
* @return string A unique identifier for this news
*/
public function getGid() {
return $this->gid;
}
/**
* Returns the title of this news item
*
* @return string The title of this news
*/
public function getTitle() {
return $this->title;
}
/**
* Returns the URL of the original news
*
* This is a direct link to the news on the Steam website or a redirecting
* link to the external post.
*
* @return string The URL of the original news
*/
public function getUrl() {
return $this->url;
}
/**
* Returns whether this news item originates from a source other than Steam
* itself (e.g. an external blog)
*
* @return boolean <var>true</var> if this news item is from an external
* source
*/
public function isExternal() {
return $this->external;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2010-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
/**
* Represents the stats for a Counter-Strike: Source map for a specific user
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class CSSMap {
/**
* @var bool
*/
private $favorite;
/**
* @var string
*/
private $name;
/**
* @var int
*/
private $roundsPlayed;
/**
* @var int
*/
private $roundsLost;
/**
* @var int
*/
private $roundsWon;
/**
* Creates a new instance of a Counter-Strike: Source class based on the
* given XML data
*
* @param string $mapName The name of the map
* @param SimpleXMLElement $mapsData The XML data of all maps
*/
public function __construct($mapName, SimpleXMLElement $mapsData) {
$this->name = $mapName;
$this->favorite = ((string) $mapsData->favorite) == $this->name;
$this->roundsPlayed = (int) $mapsData->{"{$this->name}_rounds"};
$this->roundsWon = (int) $mapsData->{"{$this->name}_wins"};
$this->roundsLost = $this->roundsPlayed - $this->roundsWon;
}
/**
* Returns whether this map is the favorite map of this player
*
* @return bool <var>true</var> if this is the favorite map
*/
public function isFavorite() {
return $this->favorite;
}
/**
* Returns the name of this map
*
* @return string The name of this map
*/
public function getName() {
return $this->name;
}
/**
* Returns the number of rounds the player has lost on this map
*
* @return int The number of rounds lost
*/
public function getRoundsLost() {
return $this->roundsLost;
}
/**
* Returns the number of rounds the player has played on this map
*
* @return int The number of rounds played
*/
public function getRoundsPlayed() {
return $this->roundsPlayed;
}
/**
* Returns the number of rounds the player has won on this map
*
* @return int The number of rounds won
*/
public function getRoundsWon() {
return $this->roundsWon;
}
/**
* Returns the percentage of rounds the player has won on this map
*
* @return float The percentage of rounds won
*/
public function getRoundsWonPercentage() {
return ($this->roundsPlayed > 0) ? ($this->roundsWon / $this->roundsPlayed) : 0;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2010-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/css/CSSMap.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/css/CSSWeapon.php';
/**
* The is class represents the game statistics for a single user in
* Counter-Strike: Source
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class CSSStats extends GameStats {
/**
* @var array The names of the maps in Counter-Strike: Source
*/
protected static $MAPS = array( 'cs_assault', 'cs_compound',
'cs_havana', 'cs_italy', 'cs_militia', 'cs_office', 'de_aztec',
'de_cbble', 'de_chateau', 'de_dust', 'de_dust2', 'de_inferno',
'de_nuke', 'de_piranesi', 'de_port', 'de_prodigy', 'de_tides',
'de_train' );
/**
* @var array The names of the weapons in Counter-Strike: Source
*/
protected static $WEAPONS = array( 'deagle', 'usp', 'glock', 'p228',
'elite', 'fiveseven', 'awp', 'ak47', 'm4a1', 'aug', 'sg552',
'sg550', 'galil', 'famas', 'scout', 'g3sg1', 'p90', 'mp5navy',
'tmp', 'mac10', 'ump45', 'm3', 'xm1014', 'm249', 'knife',
'grenade' );
private $lastMatchStats;
private $totalStats;
/**
* Creates a <var>CSSStats</var> instance by calling the super constructor
* with the game name <var>"cs:s"</var>
*
* @param string $steamId The custom URL or 64bit Steam ID of the user
*/
public function __construct($steamId) {
parent::__construct($steamId, 'cs:s');
if($this->isPublic()) {
$statsData = $this->xmlData->stats;
$this->lastMatchStats = array();
$this->totalStats = array();
$this->lastMatchStats['costPerKill'] = (float) $statsData->lastmatch->costkill;
$this->lastMatchStats['ctWins'] = (int) $statsData->lastmatch->ct_wins;
$this->lastMatchStats['damage'] = (int) $statsData->lastmatch->dmg;
$this->lastMatchStats['deaths'] = (int) $statsData->lastmatch->deaths;
$this->lastMatchStats['dominations'] = (int) $statsData->lastmatch->dominations;
$this->lastMatchStats['favoriteWeaponId'] = (int) $statsData->lastmatch->favwpnid;
$this->lastMatchStats['kills'] = (int) $statsData->lastmatch->kills;
$this->lastMatchStats['maxPlayers'] = (int) $statsData->lastmatch->max_players;
$this->lastMatchStats['money'] = (int) $statsData->lastmatch->money;
$this->lastMatchStats['revenges'] = (int) $statsData->lastmatch->revenges;
$this->lastMatchStats['stars'] = (int) $statsData->lastmatch->stars;
$this->lastMatchStats['tWins'] = (int) $statsData->lastmatch->t_wins;
$this->lastMatchStats['wins'] = (int) $statsData->lastmatch->wins;
$this->totalStats['blindKills'] = (int) $statsData->lifetime->blindkills;
$this->totalStats['bombsDefused'] = (int) $statsData->lifetime->bombsdefused;
$this->totalStats['bombsPlanted'] = (int) $statsData->lifetime->bombsplanted;
$this->totalStats['damage'] = (int) $statsData->lifetime->dmg;
$this->totalStats['deaths'] = (int) $statsData->summary->deaths;
$this->totalStats['dominationOverkills'] = (int) $statsData->lifetime->dominationoverkills;
$this->totalStats['dominations'] = (int) $statsData->lifetime->dominations;
$this->totalStats['earnedMoney'] = (int) $statsData->lifetime->money;
$this->totalStats['enemyWeaponKills'] = (int) $statsData->lifetime->enemywpnkills;
$this->totalStats['headshots'] = (int) $statsData->lifetime->headshots;
$this->totalStats['hits'] = (int) $statsData->summary->shotshit;
$this->totalStats['hostagesRescued'] = (int) $statsData->lifetime->hostagesrescued;
$this->totalStats['kills'] = (int) $statsData->summary->kills;
$this->totalStats['knifeKills'] = (int) $statsData->lifetime->knifekills;
$this->totalStats['logosSprayed'] = (int) $statsData->lifetime->decals;
$this->totalStats['nightvisionDamage'] = (int) $statsData->lifetime->nvgdmg;
$this->totalStats['pistolRoundsWon'] = (int) $statsData->lifetime->pistolrounds;
$this->totalStats['revenges'] = (int) $statsData->lifetime->revenges;
$this->totalStats['roundsPlayed'] = (int) $statsData->summary->rounds;
$this->totalStats['roundsWon'] = (int) $statsData->summary->wins;
$this->totalStats['secondsPlayed'] = (int) $statsData->summary->timeplayed;
$this->totalStats['shots'] = (int) $statsData->summary->shots;
$this->totalStats['stars'] = (int) $statsData->summary->stars;
$this->totalStats['weaponsDonated'] = (int) $statsData->lifetime->wpndonated;
$this->totalStats['windowsBroken'] = (int) $statsData->lifetime->winbroken;
$this->totalStats['zoomedSniperKills'] = (int) $statsData->lifetime->zsniperkills;
$this->lastMatchStats['kdratio'] = ($this->totalStats['deaths'] > 0) ? $this->lastMatchStats['kills'] / $this->lastMatchStats['deaths'] : 0;
$this->totalStats['accuracy'] = ($this->totalStats['shots'] > 0) ? $this->totalStats['hits'] / $this->totalStats['shots'] : 0;
$this->totalStats['kdratio'] = ($this->totalStats['deaths'] > 0) ? $this->totalStats['kills'] / $this->totalStats['deaths'] : 0;
$this->totalStats['roundsLost'] = $this->totalStats['roundsPlayed'] - $this->totalStats['roundsWon'];
}
}
/**
* Returns statistics about the last match the player played
*
* @return array The stats of the last match
*/
public function getLastMatchStats() {
return $this->lastMatchStats;
}
/**
* Returns a map of <var>CSSMap</var> for this user containing all CS:S
* maps.
*
* If the maps haven't been parsed already, parsing is done now.
*
* @return array The map statistics for this user
*/
public function getMapStats() {
if(!$this->isPublic()) {
return null;
}
if($this->mapStats == null) {
$this->mapStats = array();
$mapsData = $this->xmlData->stats->maps;
foreach(self::$MAPS as $mapName) {
$this->mapStats[$mapName] = new CSSMap($mapName, $mapsData);
}
}
return $this->mapStats;
}
/**
* Returns overall statistics of this player
*
* @return array The overall statistics
*/
public function getTotalStats() {
return $this->totalStats;
}
/**
* Returns a map of <var>CSSWeapon</var> for this user containing all CS:S
* weapons.
*
* If the weapons haven't been parsed already, parsing is done now.
*
* @return array The weapon statistics for this user
*/
public function getWeaponStats() {
if(!$this->isPublic()) {
return null;
}
if($this->weaponStats == null) {
$this->weaponStats = array();
$weaponData = $this->xmlData->stats->weapons;
foreach(self::$WEAPONS as $weaponName) {
$this->weaponStats[$weaponName] = new CSSWeapon($weaponName, $weaponData);
}
}
return $this->weaponStats;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2010-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
/**
* Represents the stats for a Counter-Strike: Source weapon for a specific user
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class CSSWeapon {
/**
* @var bool
*/
private $favorite;
/**
* @var int
*/
private $hits;
/**
* @var int
*/
private $kills;
/**
* @var string
*/
private $name;
/**
* @var int
*/
private $shots;
/**
* Creates a new instance of a Counter-Strike: Source weapon based on the
* given XML data
*
* @param string $weaponName The name of the weapon
* @param SimpleXMLElement $weaponsData The XML data of all weapons
*/
public function __construct($weaponName, SimpleXMLElement $weaponsData) {
$this->name = $weaponName;
$this->favorite = ((string) $weaponsData->favorite) == $this->name;
$this->kills = (int) $weaponsData->{"{$this->name}_kills"};
if($this->name != 'grenade' && $this->name != 'knife') {
$this->hits = (int) $weaponsData->{"{$this->name}_hits"};
$this->shots = (int) $weaponsData->{"{$this->name}_shots"};
}
}
/**
* Returns whether this weapon is the favorite weapon of this player
*
* @return bool <var>true</var> if this is the favorite weapon
*/
public function isFavorite() {
return $this->favorite;
}
/**
* Returns the accuracy of this player with this weapon
*
* @return float The accuracy with this weapon
*/
public function getAccuracy() {
return ($this->shots > 0) ? $this->hits / $this->shots : 0;
}
/**
* Returns the number of hits achieved with this weapon
*
* @return int The number of hits achieved
*/
public function getHits() {
return $this->hits;
}
/**
* Returns the number of kills achieved with this weapon
*
* @return int The number of kills achieved
*/
public function getKills() {
return $this->kills;
}
/**
* Returns the kill-shot-ratio of this player with this weapon
*
* @return float The kill-shot-ratio
*/
public function getKsRatio() {
return ($this->shots > 0) ? $this->kills / $this->shots : 0;
}
/**
* Returns the name of this weapon
*
* @return string The name of this weapon
*/
public function getName() {
return $this->name;
}
/**
* Returns the number of shots fired with this weapon
*
* @return int The number of shots fired
*/
public function getShots() {
return $this->shots;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2009-2012, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/GameStats.php';
/**
* This class represents the game statistics for a single user in Defense Grid:
* The Awakening
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class DefenseGridStats extends GameStats {
/**
* @var array
*/
private $alienStats;
/**
* @var int
*/
private $bronzeMedals;
/**
* @var float
*/
private $damage;
/**
* @var float
*/
private $damageCampaign;
/**
* @var float
*/
private $damageChallenge;
/**
* @var int
*/
private $encountered;
/**
* @var int
*/
private $goldMedals;
/**
* @var float
*/
private $heatDamage;
/**
* @var int
*/
private $interest;
/**
* @var int
*/
private $killed;
/**
* @var int
*/
private $killedCampaign;
/**
* @var int
*/
private $killedChallenge;
/**
* @var int
*/
private $levelsPlayed;
/**
* @var int
*/
private $levelsPlayedCampaign;
/**
* @var int
*/
private $levelsPlayedChallenge;
/**
* @var int
*/
private $levelsWon;
/**
* @var int
*/
private $levelsWonCampaign;
/**
* @var int
*/
private $levelsWonChallenge;
/**
* @var int
*/
private $silverMedals;
/**
* @var float
*/
private $orbitalLaserDamage;
/**
* @var int
*/
private $orbitalLaserFired;
/**
* @var int
*/
private $resources;
/**
* @var float
*/
private $timePlayed;
/**
* @var array
*/
private $towerStats;
/**
* Creates a <var>DefenseGridStats</var> object by calling the super
* constructor with the game name <var>"defensegrid:awakening"</var>
*
* @param string $steamId The custom URL or the 64bit Steam ID of the user
*/
public function __construct($steamId) {
parent::__construct($steamId, 'defensegrid:awakening');
if($this->isPublic()) {
$generalData = $this->xmlData->stats->general;
$this->bronzeMedals = (int) $generalData->bronze_medals_won->value;
$this->silverMedals = (int) $generalData->silver_medals_won->value;
$this->goldMedals = (int) $generalData->gold_medals_won->value;
$this->levelsPlayed = (int) $generalData->levels_played_total->value;
$this->levelsPlayedCampaign = (int) $generalData->levels_played_campaign->value;
$this->levelsPlayedChallenge = (int) $generalData->levels_played_challenge->value;
$this->levelsWon = (int) $generalData->levels_won_total->value;
$this->levelsWonCampaign = (int) $generalData->levels_won_campaign->value;
$this->levelsWonChallenge = (int) $generalData->levels_won_challenge->value;
$this->encountered = (int) $generalData->total_aliens_encountered->value;
$this->killed = (int) $generalData->total_aliens_killed->value;
$this->killedCampaign = (int) $generalData->total_aliens_killed_campaign->value;
$this->killedChallenge = (int) $generalData->total_aliens_killed_challenge->value;
$this->resources = (int) $generalData->resources_recovered->value;
$this->heatDamage = (float) $generalData->heatdamage->value;
$this->timePlayed = (float) $generalData->time_played->value;
$this->interest = (float) $generalData->interest_gained->value;
$this->damage = (float) $generalData->tower_damage_total->value;
$this->damageCampaign = (float) $generalData->tower_damage_total_campaign->value;
$this->damageChallenge = (float) $generalData->tower_damage_total_challenge->value;
$this->orbitalLaserFired = (int) $this->xmlData->stats->orbitallaser->fired->value;
$this->orbitalLaserDamage = (float) $this->xmlData->stats->orbitallaser->damage->value;
}
}
/**
* Returns stats about the aliens encountered by the player
*
* The array returned uses the names of the aliens as keys. Every value of
* the array is an array containing the number of aliens encountered as the
* first element and the number of aliens killed as the second element.
*
* @return array Stats about the aliens encountered
*/
public function getAlienStats() {
if(!$this->isPublic()) {
return null;
}
if(empty($this->alienStats)) {
$alienData = $this->xmlData->stats->aliens;
$this->alienStats = array();
$aliens = array('bulwark', 'crasher', 'dart', 'decoy', 'drone',
'grunt', 'juggernaut', 'manta', 'racer', 'rumbler', 'seeker',
'spire', 'stealth', 'swarmer', 'turtle', 'walker');
foreach($aliens as $alien) {
$this->alienStats[$alien] = array(
(int) $alienData->$alien->encountered->value,
(int) $alienData->$alien->killed->value
);
}
}
return $this->alienStats;
}
/**
* Returns the bronze medals won by this player
*
* @return int Bronze medals won
*/
public function getBronzeMedals() {
return $this->bronzeMedals;
}
/**
* Returns the damage done by this player
*
* @return float Damage done
*/
public function getDamage() {
return $this->damage;
}
/**
* Returns the damage done during the campaign by this player
*
* @return float Damage done during the campaign
*/
public function getDamageCampaign() {
return $this->damageCampaign;
}
/**
* Returns the damage done during challenges by this player
*
* @return float Damage done during challenges
*/
public function getDamageChallenge() {
return $this->damageChallenge;
}
/**
* Returns the aliens encountered by this player
*
* @return int Aliens encountered
*/
public function getEncountered() {
return $this->encountered;
}
/**
* Returns the gold medals won by this player
*
* @return int Gold medals won
*/
public function getGoldMedals() {
return $this->goldMedals;
}
/**
* Returns the heat damage done by this player
*
* @return float Heat damage done
*/
public function getHeatDamage() {
return $this->heatDamage;
}
/**
* Returns the interest gained by the player
*
* @return int Interest gained
*/
public function getInterest() {
return $this->interest;
}
/**
* Returns the aliens killed by the player
*
* @return int Aliens killed
*/
public function getKilled() {
return $this->killed;
}
/**
* Returns the aliens killed during the campaign by the player
*
* @return int Aliens killed during the campaign
*/
public function getKilledCampaign() {
return $this->killedCampaign;
}
/**
* Returns the aliens killed during challenges by the player
*
* @return int Aliens killed during challenges
*/
public function getKilledChallenge() {
return $this->killedChallenge;
}
/**
* Returns the number of levels played by the player
*
* @return int Number of levels played
*/
public function getLevelsPlayed() {
return $this->levelsPlayed;
}
/**
* Returns the number of levels played during the campaign by the player
*
* @return int Number of levels played during the campaign
*/
public function getLevelsPlayedCampaign() {
return $this->levelsPlayedCampaign;
}
/**
* Returns the number of levels played during challenges by the player
*
* @return int Number of levels played during challenges
*/
public function getLevelsPlayedChallenge() {
return $this->levelsPlayedChallenge;
}
/**
* Returns the number of levels won by the player
*
* @return int Number of levels won
*/
public function getLevelsWon() {
return $this->levelsWon;
}
/**
* Returns the number of levels won during the campaign by the player
*
* @return int Number of levels during the campaign won
*/
public function getLevelsWonCampaign() {
return $this->levelsWonCampaign;
}
/**
* Returns the number of levels won during challenges by the player
*
* @return int Number of levels during challenges won
*/
public function getLevelsWonChallenge() {
return $this->levelsWonChallenge;
}
/**
* Returns the damage dealt by the orbital laser
*
* @return float Damage dealt by the orbital laser
*/
public function getOrbitalLaserDamage() {
return $this->orbitalLaserDamage;
}
/**
* Returns the number of times the orbital lasers has been fired by the
* player
*
* @return int Number of times the orbital laser has been fired
*/
public function getOrbitalLaserFired() {
return $this->orbitalLaserFired;
}
/**
* Returns the amount of resources harvested by the player
*
* @return int Resources harvested by the player
*/
public function getResources() {
return $this->resources;
}
/**
* Returns the silver medals won by this player
*
* @return int Silver medals won
*/
public function getSilverMedals() {
return $this->silverMedals;
}
/**
* Returns the time played in seconds by the player
*
* @return float Time played
*/
public function getTimePlayed() {
return $this->timePlayed;
}
/**
* Returns stats about the towers built by the player
*
* The array returned uses the names of the towers as keys. Every value of
* the array is another array using the keys 1 to 3 for different tower
* levels.
* The values of these arrays is an array containing the number of towers
* built as the first element and the damage dealt by this specific tower
* type as the second element.
*
* The Command tower uses the resources gained as second element.
* The Temporal tower doesn't have a second element.
*
* @return array Stats about the towers built
*/
public function getTowerStats() {
if(!$this->isPublic()) {
return null;
}
if(empty($this->towerStats)) {
$towerData = $this->xmlData->stats->towers;
$this->towerStats = array();
$towers = array('cannon', 'flak', 'gun', 'inferno', 'laser',
'meteor', 'missile', 'tesla');
foreach($towers as $tower) {
$this->towerStats[$tower] = array();
for($i = 1; $i <= 3; $i++) {
$built = $towerData->xpath("{$tower}[@level=$i]/built/value");
$damage = $towerData->xpath("{$tower}[@level=$i]/damage/value");
$this->towerStats[$tower][$i] = array(
(int) $built[0],
(float) $damage[0]
);
}
}
$this->towerStats['command'] = array();
for($i = 1; $i <= 3; $i++) {
$built = $towerData->xpath("command[@level=$i]/built/value");
$resources = $towerData->xpath("command[@level=$i]/resource/value");
$this->towerStats['command'][$i] = array(
(int) $built[0],
(float) $resources[0]
);
}
$this->towerStats['temporal'] = array();
for($i = 1; $i <= 3; $i++) {
$built = $towerData->xpath("temporal[@level=$i]/built/value");
$this->towerStats['temporal'][$i] = array(
(int) $built[0]
);
}
}
return $this->towerStats;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2009-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/GameClass.php';
/**
* Represents the stats for a Day of Defeat: Source class for a specific user
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class DoDSClass extends GameClass {
/**
* @var int
*/
private $blocks;
/**
* @var int
*/
private $bombsDefused;
/**
* @var int
*/
private $bombsPlanted;
/**
* @var int
*/
private $captures;
/**
* @var int
*/
private $deaths;
/**
* @var int
*/
private $dominations;
/**
* @var string
*/
private $key;
/**
* @var int
*/
private $kills;
/**
* @var int
*/
private $roundsLost;
/**
* @var int
*/
private $roundsWon;
/**
* @var int
*/
private $revenges;
/**
* Creates a new instance of a Day of Defeat: Source class based on the
* given XML data
*
* @param SimpleXMLElement $classData The XML data of the class
*/
public function __construct(SimpleXMLElement $classData) {
$this->blocks = (int) $classData->blocks;
$this->bombsDefused = (int) $classData->bombsdefused;
$this->bombsPlanted = (int) $classData->bombsplanted;
$this->captures = (int) $classData->captures;
$this->deaths = (int) $classData->deaths;
$this->dominations = (int) $classData->dominations;
$this->key = (string) $classData['key'];
$this->kills = (int) $classData->kills;
$this->name = (string) $classData->name;
$this->playTime = (int) $classData->playtime;
$this->roundsLost = (int) $classData->roundslost;
$this->roundsWon = (int) $classData->roundswon;
$this->revenges = (int) $classData->revenges;
}
/**
* Returns the blocks achieved by the player with this class
*
* @return int The blocks achieved by the player
*/
public function getBlocks() {
return $this->blocks;
}
/**
* Returns the bombs defused by the player with this class
*
* @return int The bombs defused by the player
*/
public function getBombsDefuse() {
return $this->bombsDefused;
}
/**
* Returns the bombs planted by the player with this class
*
* @return int the bombs planted by the player
*/
public function getBombsPlanted() {
return $this->bombsPlanted;
}
/**
* Returns the number of points captured by the player with this class
*
* @return int The number of points captured by the player
*/
public function getCaptures() {
return $this->captures;
}
/**
* Returns the number of times the player died with this class
*
* @return int The number of deaths by the player
*/
public function getDeaths() {
return $this->deaths;
}
/**
* Returns the dominations achieved by the player with this class
*
* @return int The dominations achieved by the player
*/
public function getDominations() {
return $this->dominations;
}
/**
* Returns the ID of this class
*
* @return string The ID of this class
*/
public function getKey() {
return $this->key;
}
/**
* Returns the number of enemies killed by the player with this class
*
* @return int The number of enemies killed by the player
*/
public function getKills() {
return $this->kills;
}
/**
* Returns the revenges achieved by the player with this class
*
* @return int The revenges achieved by the player
*/
public function getRevenges() {
return $this->revenges;
}
/**
* Returns the number of rounds lost with this class
*
* @return int The number of rounds lost with this class
*/
public function getRoundsLost() {
return $this->roundsLost;
}
/**
* Returns the number of rounds won with this class
*
* @return int The number of rounds won with this class
*/
public function getRoundsWon() {
return $this->roundsWon;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2009-2012, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/GameStats.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/dods/DoDSClass.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/dods/DoDSWeapon.php';
/**
* The is class represents the game statistics for a single user in Day of
* Defeat: Source
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class DoDSStats extends GameStats {
private $classStats;
private $weaponStats;
/**
* Creates a <var>DoDSStats</var> instance by calling the super constructor
* with the game name <var>"DoD:S"</var>
*
* @param string $steamId The custom URL or 64bit Steam ID of the user
*/
public function __construct($steamId) {
parent::__construct($steamId, 'DoD:S');
}
/**
* Returns an array of <var>DoDSClass</var> for this user containing all
* DoD:S classes.
*
* If the classes haven't been parsed already, parsing is done now.
*
* @return array The class statistics for this user
*/
public function getClassStats() {
if(!$this->isPublic()) {
return null;
}
if(empty($this->classStats)) {
$this->classStats = array();
foreach($this->xmlData->stats->classes->children() as $classData) {
$this->classStats[(string) $classData['key']] = new DoDSClass($classData);
}
}
return $this->classStats;
}
/**
* Returns an array of <var>DoDSWeapon</var> for this user containing all
* DoD:S weapons.
*
* If the weapons haven't been parsed already, parsing is done now.
*
* @return array The weapon statistics for this user
*/
public function getWeaponStats() {
if(!$this->isPublic()) {
return null;
}
if(empty($this->weaponStats)) {
foreach($this->xmlData->stats->weapons->children() as $classData) {
$this->weaponStats[(string) $classData['key']] = new DoDSWeapon($classData);
}
}
return $this->weaponStats;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2009-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/GameWeapon.php';
/**
* Represents the stats for a Day of Defeat: Source weapon for a specific user
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class DoDSWeapon extends GameWeapon {
/**
* @var int
*/
private $headshots;
/**
* @var int
*/
private $hits;
/**
* @var string
*/
private $name;
/**
* Creates a new instance of a Day of Defeat: Source weapon based on the
* given XML data
*
* @param SimpleXMLElement $weaponData The XML data of the class
*/
public function __construct(SimpleXMLElement $weaponData) {
parent::__construct($weaponData);
$this->headshots = (int) $weaponData->headshots;
$this->id = (string) $weaponData['key'];
$this->name = (string) $weaponData->name;
$this->shots = (int) $weaponData->shotsfired;
$this->hits = (int) $weaponData->shotshit;
}
/**
* Returns the average number of hits needed for a kill with this weapon
*
* @return float The average number of hits needed for a kill
*/
public function getAvgHitsPerKill() {
return $this->hits / $this->kills;
}
/**
* Returns the percentage of headshots relative to the shots hit with this
* weapon
*
* @return float The percentage of headshots
*/
public function getHeadshotPercentage() {
return $this->headshots / $this->hits;
}
/**
* Returns the number of headshots achieved with this weapon
*
* @return int The number of headshots achieved
*/
public function getHeadshots() {
return $this->headshots;
}
/**
* Returns the percentage of hits relative to the shots fired with this
* weapon
*
* @return float The percentage of hits
*/
public function getHitPercentage() {
return$this->hits / $this->shots;
}
/**
* Returns the number of hits achieved with this weapon
*
* @return int The number of hits achieved
*/
public function getHits() {
return $this->hits;
}
/**
* Returns the name of this weapon
*
* @return string The name of this weapon
*/
public function getName() {
return $this->name;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2012, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/GameInventory.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/dota2/DotA2Item.php';
/**
* Represents the inventory of a player of the DotA 2 beta
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class DotA2BetaInventory extends GameInventory {
const APP_ID = 205790;
const ITEM_CLASS = 'DotA2Item';
/**
* This checks the cache for an existing inventory. If it exists it is
* returned. Otherwise a new inventory is created.
*
* @param string $steamId The 64bit Steam ID or vanity URL of the user
* @param bool $fetchNow Whether the data should be fetched now
* @param bool $bypassCache Whether the cache should be bypassed
* @return DotA2BetaInventory The inventory created from the given options
*/
public static function createInventory($steamId, $fetchNow = true, $bypassCache = false) {
return parent::create(self::APP_ID, $steamId, $fetchNow, $bypassCache);
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2012, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/GameInventory.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/dota2/DotA2Item.php';
/**
* Represents the inventory of a DotA 2 player
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class DotA2Inventory extends GameInventory {
const APP_ID = 570;
const ITEM_CLASS = 'DotA2Item';
/**
* This checks the cache for an existing inventory. If it exists it is
* returned. Otherwise a new inventory is created.
*
* @param string $steamId The 64bit Steam ID or vanity URL of the user
* @param bool $fetchNow Whether the data should be fetched now
* @param bool $bypassCache Whether the cache should be bypassed
* @return DotA2Inventory The inventory created from the given options
*/
public static function createInventory($steamId, $fetchNow = true, $bypassCache = false) {
return parent::create(self::APP_ID, $steamId, $fetchNow, $bypassCache);
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2012-2013, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/GameItem.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/dota2/DotA2Inventory.php';
/**
* Represents a DotA 2 item
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class DotA2Item extends GameItem {
/**
* @var bool
*/
private $equipped;
/**
* Creates a new instance of a DotA2Item with the given data
*
* @param DotA2Inventory $inventory The inventory this item is contained
* in
* @param array $itemData The data specifying this item
* @throws WebApiException on Web API errors
*/
public function __construct(DotA2Inventory $inventory, $itemData) {
parent::__construct($inventory, $itemData);
$this->equipped = property_exists($itemData, 'equipped') && sizeof($itemData->equipped) > 0;
}
/**
* Returns whether this item is equipped by this player at all
*
* @return bool Whether this item is equipped by this player at all
*/
public function isEquipped() {
return $this->equipped;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2012, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/WebApi.php';
/**
* The GameAchievement class represents a specific achievement for a single
* game and for a single user
*
* It also provides the ability to load the global unlock percentages of all
* achievements of a specific game.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class GameAchievement {
/**
* @var string
*/
private $apiName;
/**
* @var SteamGame
*/
private $game;
/**
* @var string
*/
private $iconClosedUrl;
/**
* @var string
*/
private $iconOpenUrl;
/**
* @var string
*/
private $name;
/**
* @var int
*/
private $timestamp;
/**
* @var SteamId
*/
private $user;
/**
* @var bool
*/
private $unlocked;
/**
* Loads the global unlock percentages of all achievements for the given
* game
*
* @param int $appId The unique Steam Application ID of the game (e.g.
* <var>440</var> for Team Fortress 2). See
* http://developer.valvesoftware.com/wiki/Steam_Application_IDs for
* all application IDs
* @return array The symbolic achievement names with the corresponding
* global unlock percentages
* @throws WebApiException if a request to Steam's Web API fails
*/
public static function getGlobalPercentages($appId) {
$params = array('gameid' => $appId);
$data = json_decode(WebApi::getJSON('ISteamUserStats', 'GetGlobalAchievementPercentagesForApp', 2, $params));
$percentages = array();
foreach($data->achievementpercentages->achievements as $achievementData) {
$percentages[$achievementData->name] = (float) $achievementData->percent;
}
return $percentages;
}
/**
* Creates the achievement with the given name for the given user and game
* and achievement data
*
* @param SteamId $user The Steam ID of the player this achievement belongs
* to
* @param SteamGame $game The game this achievement belongs to
* @param SimpleXMLElement $achievementData The achievement data extracted
* from XML
*/
public function __construct(SteamId $user, SteamGame $game, SimpleXMLElement $achievementData) {
$this->apiName = (string) $achievementData->apiname;
$this->description = (string) $achievementData->description;
$this->game = $game;
$this->iconClosedUrl = (string) $achievementData->iconClosed;
$this->iconOpenUrl = (string) $achievementData->iconOpen;
$this->name = (string) $achievementData->name;
$this->unlocked = (bool)(int) $achievementData->attributes()->closed;
$this->user = $user;
if($this->unlocked && $achievementData->unlockTimestamp != null) {
$this->timestamp = (int) $achievementData->unlockTimestamp;
}
}
/**
* Returns the symbolic API name of this achievement
*
* @return string The API name of this achievement
*/
public function getApiName() {
return $this->apiName;
}
/**
* Returns the description of this achievement
*
* @return string The description of this achievement
*/
public function getDescription() {
return $this->description;
}
/**
* Returns the game this achievement belongs to
*
* @return SteamGame The game this achievement belongs to
*/
public function getGame() {
return $this->game;
}
/**
* Returns the url for the closed icon of this achievement
*
* @return string The url of the closed achievement icon
*/
public function getIconClosedUrl() {
return $this->iconClosedUrl;
}
/**
* Returns the url for the open icon of this achievement
*
* @return string The url of the open achievement icon
*/
public function getIconOpenUrl() {
return $this->iconOpenUrl;
}
/**
* Returns the name of this achievement
*
* @return string The name of this achievement
*/
public function getName() {
return $this->name;
}
/**
* Returns the time this achievement has been unlocked by its owner
*
* @return int The time this achievement has been unlocked
*/
public function getTimestamp() {
return $this->timestamp;
}
/**
* Returns the SteamID of the user who owns this achievement
*
* @return SteamId The SteamID of this achievement's owner
*/
public function getUser() {
return $this->user;
}
/**
* Returns whether this achievement has been unlocked by its owner
*
* @return bool <var>true</var> if the achievement has been unlocked by the
* user
*/
public function isUnlocked() {
return $this->unlocked;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2009-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
/**
* An abstract class implementing basic functionality for classes representing
* player classes
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
abstract class GameClass {
/**
* @var String
*/
protected $name;
/**
* @var float
*/
protected $playtime;
/**
* Returns the name of this class
*
* @return string The name of this class
*/
public function getName() {
return $this->name;
}
/**
* Returns the time in minutes the player has played with this class
*
* @return int The time this class has been played
*/
public function getPlayTime() {
return $this->playtime;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2011-2013, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/GameItem.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/GameItemSchema.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/SteamId.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/WebApi.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/dota2/DotA2BetaInventory.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/dota2/DotA2Inventory.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/portal2/Portal2Inventory.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/tf2/TF2BetaInventory.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/tf2/TF2Inventory.php';
/**
* Provides basic functionality to represent an inventory of player in a game
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class GameInventory {
const ITEM_CLASS = 'GameItem';
/**
* @var array
*/
public static $cache = array();
/**
* @var string
*/
public static $schemaLanguage = 'en';
/**
* @var int
*/
protected $appId;
/**
* @var int
*/
protected $fetchDate;
/**
* @var GameItemSchema
*/
protected $itemSchema;
/**
* @var array
*/
protected $items;
/**
* @var array
*/
protected $preliminaryItems;
/**
* @var string
*/
protected $steamId64;
/**
* @var SteamId
*/
protected $user;
/**
* Clears the inventory cache
*/
public static function clearCache() {
self::$cache = array();
}
/**
* This checks the cache for an existing inventory. If it exists it is
* returned. Otherwise a new inventory is created.
*
* @param int $appId The application ID of the game
* @param string $steamId The 64bit Steam ID or the vanity URL of the user
* @param bool $fetchNow Whether the data should be fetched now
* @param bool $bypassCache Whether the cache should be bypassed
* @return GameInventory
*/
public static function create($appId, $steamId, $fetchNow = true, $bypassCache = false) {
if (is_numeric($steamId)) {
$steamId64 = $steamId;
} else {
$steamId64 = SteamId::resolveVanityUrl($steamId);
}
if (self::isCached($appId, $steamId64) && !$bypassCache) {
$inventory = self::$cache[$appId][$steamId64];
if ($fetchNow && !$inventory->isFetched()) {
$inventory->fetch();
}
return $inventory;
} else {
switch ($appId) {
case Dota2BetaInventory::APP_ID:
$inventoryClass = 'Dota2BetaInventory';
break;
case Dota2Inventory::APP_ID:
$inventoryClass = 'Dota2Inventory';
break;
case Portal2Inventory::APP_ID:
$inventoryClass = 'Portal2Inventory';
break;
case TF2BetaInventory::APP_ID:
$inventoryClass = 'TF2BetaInventory';
break;
case TF2Inventory::APP_ID:
$inventoryClass = 'TF2Inventory';
break;
default:
$inventoryClass = 'GameInventory';
}
return new $inventoryClass($appId, $steamId64, $fetchNow);
}
}
/**
* Returns whether the requested inventory is already cached
*
* @param int $appId The application ID of the game
* @param string $steamId64 The 64bit Steam ID of the user
* @return bool <var>true</var> if the inventory of the given user for the
* given game is already cached
*/
public static function isCached($appId, $steamId64) {
return array_key_exists($appId, self::$cache) &&
array_key_exists($steamId64, self::$cache[$appId]);
}
/**
* Sets the language the schema should be fetched in (default is:
* <var>'en'</var>)
*
* @param string $language The language code for the language item
* descriptions should be fetched in
*/
public static function setSchemaLanguage($language) {
self::$schemaLanguage = $language;
}
/**
* Creates a new inventory object for the given user. This calls
* <var>fetch()</var> to update the data and create the GameItem instances
* contained in this player's inventory
*
* @param int $appId The application ID of the game
* @param string $steamId64 The 64bit Steam ID of the user
* @param bool $fetchNow Whether the data should be fetched now
* @throws WebApiException on Web API errors
*/
protected function __construct($appId, $steamId64, $fetchNow = true) {
$this->appId = $appId;
$this->steamId64 = $steamId64;
$this->user = SteamId::create($steamId64, false);
if ($fetchNow) {
$this->fetch();
}
$this->cache();
array_keys(self::$cache);
array_keys(self::$cache[$appId]);
}
/**
* Saves this inventory in the cache
*/
public function cache() {
self::$cache[$this->appId][$this->steamId64] = $this;
}
/**
* Updates the contents of the backpack using Steam Web API
*/
public function fetch() {
$params = array('SteamID' => $this->steamId64);
$result = WebApi::getJSONData("IEconItems_{$this->getAppId()}", 'GetPlayerItems', 1, $params);
$this->items = array();
$this->preliminaryItems = array();
foreach ($result->items as $itemData) {
if ($itemData != null) {
$inventoryClass = new ReflectionClass(get_class($this));
$itemClass = $inventoryClass->getConstant('ITEM_CLASS');
$item = new $itemClass($this, $itemData);
if ($item->isPreliminary()) {
$this->preliminaryItems[] = $item;
} else {
$this->items[$item->getBackpackPosition() - 1] = $item;
}
}
}
$this->fetchDate = time();
}
/**
* Returns the application ID of the game this inventory belongs to
*
* @return int The application ID of the game this inventory belongs to
*/
public function getAppId() {
return $this->appId;
}
/**
* Returns the item at the given position in the backpack. The positions
* range from 1 to 100 instead of the usual array indices (0 to 99).
*
* @param int $index The position of the item in the backpack
* @return GameItem The item at the given position
*/
public function getItem($index) {
return $this->items[$index - 1];
}
/**
* Returns the item schema
*
* The item schema is fetched first if not done already
*
* @return GameItemSchema The item schema for the game this inventory belongs to
* @throws WebApiException on Web API errors
*/
public function getItemSchema() {
if ($this->itemSchema == null) {
$this->itemSchema = GameItemSchema::create($this->appId, self::$schemaLanguage);
}
return $this->itemSchema;
}
/**
* Returns an array of all items in this players inventory.
*
* @return array All items in the backpack
*/
public function getItems() {
return $this->items;
}
/**
* Returns an array of all items that this player just found or traded
*
* @return array All preliminary items of the inventory
*/
public function getPreliminaryItems() {
return $this->preliminaryItems;
}
/**
* Returns the Steam ID of the player owning this inventory
*
* @return SteamId The Steam ID of the owner of this inventory
*/
public function getUser() {
return $this->user;
}
/**
* Returns the 64bit SteamID of the player owning this inventory
*
* @return string The 64bit SteamID
*/
public function getSteamId64() {
return $this->steamId64;
}
/**
* Returns whether the items contained in this inventory have been already
* fetched
*
* @return bool Whether the contents backpack have been fetched
*/
public function isFetched() {
return !empty($this->fetchDate);
}
/**
* Returns the number of items in the user's backpack
*
* @return int The number of items in the backpack
*/
public function size() {
return sizeof($this->items);
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2011-2013, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/GameItem.php';
/**
* Provides basic functionality to represent an item in a game
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class GameItem {
/**
* @var array
*/
private $attributes;
/**
* @var int
*/
private $backpackPosition;
/**
* @var int
*/
private $count;
/**
* @var bool
*/
private $craftable;
/**
* @var int
*/
private $defindex;
/**
* @var int
*/
private $id;
/**
* @var string
*/
private $itemClass;
/**
* @var int
*/
private $level;
/**
* @var string
*/
private $name;
/**
* @var string
*/
private $origin;
/**
* @var int
*/
private $originalId;
/**
* @var bool
*/
private $preliminary;
/**
* @var string
*/
private $quality;
/**
* @var bool
*/
private $tradeable;
/**
* @var string
*/
private $type;
/**
* Creates a new instance of a GameItem with the given data
*
* @param GameInventory $inventory The inventory this item is contained in
* @param stdClass $itemData The data specifying this item
* @throws WebApiException on Web API errors
*/
public function __construct(GameInventory $inventory, $itemData) {
$this->inventory = $inventory;
$this->defindex = $itemData->defindex;
$this->backpackPosition = $itemData->inventory & 0xffff;
$this->count = $itemData->quantity;
$this->id = $itemData->id;
$this->itemClass = $this->getSchemaData()->item_class;
$this->level = $itemData->level;
$this->name = $this->getSchemaData()->item_name;
$origins = $this->inventory->getItemSchema()->getOrigins();
$this->originalId = $itemData->original_id;
$this->preliminary = ($itemData->inventory & 0x40000000) != 0;
$qualities = $this->inventory->getItemSchema()->getQualities();
$this->quality = $qualities[$itemData->quality];
$this->type = $this->getSchemaData()->item_type_name;
if (property_exists($itemData, 'flag_cannot_craft')) {
$this->craftable = !!$itemData->flag_cannot_craft;
}
if (!empty($this->getSchemaData()->item_set)) {
$itemSets = $this->inventory->getItemSchema()->getItemSets();
$this->itemSet = $itemSets[$this->getSchemaData()->item_set];
}
if (property_exists($itemData, 'origin')) {
$this->origin = $origins[$itemData->origin];
}
if (property_exists($itemData, 'flag_cannot_trade')) {
$this->tradeable = !!$itemData->flag_cannot_trade;
}
$attributesData = array();
if (property_exists($this->getSchemaData(), 'attributes')) {
$attributesData = (array) $this->getSchemaData()->attributes;
}
if (!empty($itemData->attributes)) {
$attributesData = array_merge_recursive($attributesData, (array) $itemData->attributes);
}
$this->attributes = array();
foreach ($attributesData as $attributeData) {
$attributeKey = property_exists($attributeData, 'defindex') ?
$attributeData->defindex : $attributeData->name;
if ($attributeKey != null) {
$schemaAttributesData = $inventory->getItemSchema()->getAttributes();
$schemaAttributeData = $schemaAttributesData[$attributeKey];
$this->attributes[] = (object) array_merge_recursive((array) $attributeData, (array) $schemaAttributeData);
}
}
}
/**
* Return the attributes of this item
*
* @return array The attributes of this item
*/
public function getAttributes() {
return $this->attributes;
}
/**
* Returns the position of this item in the player's inventory
*
* @return int The position of this item in the player's inventory
*/
public function getBackpackPosition() {
return $this->backpackPosition;
}
/**
* Returns the number of items the player owns of this item
*
* @return int The quanitity of this item
*/
public function getCount() {
return $this->count;
}
/**
* Returns the index where the item is defined in the schema
*
* @return int The schema index of this item
*/
public function getDefIndex() {
return $this->defindex;
}
/**
* Returns the ID of this item
*
* @return int The ID of this item
*/
public function getId() {
return $this->id;
}
/**
* Returns the class of this item
*
* @return string The class of this item
*/
public function getItemClass() {
return $this->itemClass;
}
/**
* Returns the level of this item
*
* @return int The level of this item
*/
public function getLevel() {
return $this->level;
}
/**
* Returns the level of this item
*
* @return string The level of this item
*/
public function getName() {
return $this->name;
}
/**
* Returns the original ID of this item
*
* @return int The original ID of this item
*/
public function getOriginalId() {
return $this->originalId;
}
/**
* Returns the quality of this item
*
* @return string The quality of this item
*/
public function getQuality() {
return $this->quality;
}
/**
* Returns the data for this item that's defined in the item schema
*
* @return array The schema data for this item
* @throws SteamCondenserException if the item schema cannot be loaded
*/
public function getSchemaData() {
$schemaItems = $this->inventory->getItemSchema()->getItems();
return $schemaItems[$this->defindex];
}
/**
* Returns the type of this item
*
* @return string The type of this item
*/
public function getType() {
return $this->type;
}
/**
* Returns whether this item is craftable
*
* @return bool <var>true</var> if this item is craftable
*/
public function isCraftable() {
return $this->craftable;
}
/**
* Returns whether this item is preliminary
*
* Preliminary means that this item was just found or traded and has not
* yet been added to the inventory
*
* @return bool <var>true</var> if this item is preliminary
*/
public function isPreliminary() {
return $this->preliminary;
}
/**
* Returns whether this item is tradeable
*
* @return bool <var>true</var> if this item is tradeable
*/
public function isTradeable() {
return $this->tradeable;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2012, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/WebApi.php';
/**
* Provides item definitions and related data that specify the items of a game
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class GameItemSchema {
/**
* @var array
*/
public static $cache = array();
/**
* @var int
*/
private $appId;
/**
* @var array
*/
private $attributes;
/**
* @var array
*/
private $effects;
/**
* @var int
*/
private $fetchDate;
/**
* @var array
*/
private $itemLevels;
/**
* @var array
*/
private $itemNames;
/**
* @var array
*/
private $itemSets;
/**
* @var array
*/
private $items;
/**
* @var string
*/
private $language;
/**
* @var array
*/
private $origins;
/**
* @var array
*/
private $qualities;
/**
* Clears the item schema cache
*/
public static function clearCache() {
self::$cache = array();
}
/**
* Creates a new item schema for the game with the given application ID and
* with descriptions in the given language
*
* @param int $appId The application ID of the game
* @param string $language The language of description strings
* @param bool $fetch if <var>true</var> the schemas's data is fetched
* after creation
* @param bool $bypassCache if <var>true</var> the schemas's data is
* fetched again even if it has been cached already
* @return GameInventory The item schema for the given game and language
*/
public static function create($appId, $language, $fetch = true, $bypassCache = false) {
if (GameItemSchema::isCached($appId, $language) && !$bypassCache) {
$itemSchema = self::$cache[$appId][$language];
if ($fetch && !$itemSchema->isFetched()) {
$itemSchema->fetch();
}
return $itemSchema;
} else {
return new GameItemSchema($appId, $language, $fetch);
}
}
/**
* Returns whether the item schema for the given application ID and
* language is already cached
*
* @param int $appId The application ID of the game
* @param string $language The language of the item schema
* @return bool <var>true</var> if the object with the given ID is already
* cached
*/
public static function isCached($appId, $language) {
return array_key_exists($appId, self::$cache) &&
array_key_exists($language, self::$cache[$appId]);
}
/**
* Creates a new item schema for the game with the given application ID and
* with descriptions in the given language
*
* @param int $appId The application ID of the game
* @param string $language The language of description strings
* @param bool $fetch if <var>true</var> the schemas's data is fetched
* after creation
*/
protected function __construct($appId, $language, $fetch) {
$this->appId = $appId;
$this->language = $language;
if ($fetch) {
$this->fetch();
}
}
/**
* Updates the item definitions of this schema using the Steam Web API
*
* @throws WebApiException if the item schema cannot be fetched
*/
public function fetch() {
$params = array('language' => $this->language);
$data = WebApi::getJSONData("IEconItems_{$this->appId}", 'GetSchema', 1, $params);
$this->attributes = array();
foreach ($data->attributes as $attribute) {
$this->attributes[$attribute->defindex] = $attribute;
$this->attributes[$attribute->name] = $attribute;
}
$this->effects = array();
foreach ($data->attribute_controlled_attached_particles as $effect) {
$this->effects[$effect->id] = $effect;
}
$this->items = array();
$this->itemNames = array();
foreach ($data->items as $item) {
$this->items[$item->defindex] = $item;
$this->itemNames[$item->name] = $item->defindex;
}
if (!empty($data->levels)) {
$this->itemLevels = array();
foreach ($data->item_levels as $itemLevelType) {
$itemLevels = array();
foreach ($itemLevelType->levels as $itemLevel) {
$itemLevels[$itemLevel->level] = $itemLevel->name;
}
$this->itemLevels[$itemLevelType->name] = $itemLevels;
}
}
$this->itemSets = array();
foreach ($data->item_sets as $itemSet) {
$this->itemSets[$itemSet->item_set] = $itemSet;
}
$this->origins = array();
foreach ($data->originNames as $origin) {
$this->origins[$origin->origin] = $origin->name;
}
$this->qualities = array();
$index = -1;
foreach ($data->qualities as $key => $value) {
$index ++;
if (property_exists($data->qualityNames, $key)) {
$qualityName = $data->qualityNames->$key;
}
if (empty($qualityName)) {
$qualityName = ucwords($key);
}
$this->qualities[$index] = $qualityName;
}
$this->cache();
$this->fetchDate = time();
}
/**
* Returns whether the data for this item schema has already been fetched
*
* @return bool <var>true</var> if this item schema's data is available
*/
public function isFetched() {
return $this->fetchDate != null;
}
/**
* Returns the application ID of the game this item schema belongs to
*
* @return int The application ID of the game
*/
public function getAppId() {
return $this->appId;
}
/**
* The attributes defined for this game's items
*
* @return array This item schema's attributes
*/
public function getAttributes() {
return $this->attributes;
}
/**
* The effects defined for this game's items
*
* @return array This item schema's effects
*/
public function getEffects() {
return $this->effects;
}
/**
* The levels defined for this game's items
*
* @return array This item schema's item levels
*/
public function getItemLevels() {
return $this->itemLevels;
}
/**
* A mapping from the item name to the item's defindex
*
* @return array The item name mapping
*/
public function getItemNames() {
return $this->itemNames;
}
/**
* The item sets defined for this game's items
*
* @return array This item schema's item sets
*/
public function getItemSets() {
return $this->itemSets;
}
/**
* The items defined for this game
*
* @return array The items in this schema
*/
public function getItems() {
return $this->items;
}
/**
* The language of this item schema
*
* @return string The language of this item schema
*/
public function getLanguage() {
return $this->language;
}
/**
* The item origins defined for this mµµ game's items
*
* @return array This item schema's origins
*/
public function getOrigins() {
return $this->origins;
}
/**
* The item qualities defined for this game's items
*
* @return array This item schema's qualities
*/
public function getQualities() {
return $this->qualities;
}
/**
* Saves this item schema in the cache
*/
private function cache() {
self::$cache[$this->appId][$this->language] = $this;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2011, Nicholas Hastings
* 2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/GameLeaderboardEntry.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/SteamId.php';
/**
* The GameLeaderboard class represents a single leaderboard for a specific
* game
*
* @author Nicholas Hastings
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class GameLeaderboard {
const LEADERBOARD_DISPLAY_TYPE_NONE = 0;
const LEADERBOARD_DISPLAY_TYPE_NUMERIC = 1;
const LEADERBOARD_DISPLAY_TYPE_SECONDS = 2;
const LEADERBOARD_DISPLAY_TYPE_MILLISECONDS = 3;
const LEADERBOARD_SORT_METHOD_NONE = 0;
const LEADERBOARD_SORT_METHOD_ASC = 1;
const LEADERBOARD_SORT_METHOD_DESC = 2;
/**
* @var array
*/
private static $leaderboards = array();
/**
* @var int
*/
protected $id;
/**
* @var string
*/
protected $url;
/**
* @var string
*/
protected $name;
/**
* @var int
*/
protected $entryCount;
/**
* @var int
*/
protected $sortMethod;
/**
* @var int
*/
protected $displayType;
/**
* Returns the leaderboard for the given game and leaderboard ID or name
*
* @param string $gameName The short name of the game
* @param mixed $id The ID or name of the leaderboard to return
* @return GameLeaderboard The matching leaderboard if available
*/
public static function getLeaderboard($gameName, $id) {
$leaderboards = self::getLeaderboards($gameName);
if(is_int($id)) {
return $leaderboards[$id];
} else {
foreach(array_values($leaderboards) as $board) {
if($board->getName() == $id) {
return $board;
}
}
}
}
/**
* Returns an array containing all of a game's leaderboards
*
* @param string $gameName The name of the game
* @return array The leaderboards for this game
*/
public static function getLeaderboards($gameName) {
if(!array_key_exists($gameName, self::$leaderboards)) {
self::loadLeaderboards($gameName);
}
return self::$leaderboards[$gameName];
}
/**
* Loads the leaderboards of the specified games into the cache
*
* @param string $gameName The short name of the game
* @throws SteamCondenserException if an error occurs while fetching the
* leaderboards
*/
private static function loadLeaderboards($gameName) {
$url = "http://steamcommunity.com/stats/$gameName/leaderboards/?xml=1";
$boardsData = new SimpleXMLElement(file_get_contents($url));
if(!empty($boardsData->error)) {
throw new SteamCondenserException((string) $boardsData->error);
}
self::$leaderboards[$gameName] = array();
foreach($boardsData->leaderboard as $boardData) {
$leaderboard = new GameLeaderboard($boardData);
self::$leaderboards[$gameName][$leaderboard->getId()] = $leaderboard;
}
}
/**
* Creates a new leaderboard instance with the given XML data
*
* @param SimpleXMLElement $boardData The XML data of the leaderboard
*/
private function __construct(SimpleXMLElement $boardData) {
$this->url = (string) $boardData->url;
$this->id = (int) $boardData->lbid;
$this->name = (string) $boardData->name;
$this->entryCount = (int) $boardData->entries;
$this->sortMethod = (int) $boardData->sortmethod;
$this->displayType = (int) $boardData->displaytype;
}
/**
* Returns the name of the leaderboard
*
* @return string The name of the leaderboard
*/
public function getName() {
return $this->name;
}
/**
* Returns the ID of the leaderboard
*
* @return int The ID of the leaderboard
*/
public function getId() {
return $this->id;
}
/**
* Returns the number of entries on this leaderboard
*
* @return int The number of entries on this leaderboard
*/
public function getEntryCount() {
return $this->entryCount;
}
/**
* Returns the method that is used to sort the entries on the leaderboard
*
* @return int The sort method
*/
public function getSortMethod() {
return $this->sortMethod;
}
/**
* Returns the display type of the scores on this leaderboard
*
* @return int The display type of the scores
*/
public function getDisplayType() {
return $this->displayType;
}
/**
* Returns the entry on this leaderboard for the user with the given
* SteamID
*
* @param mixed $steamId The 64bit SteamID or the <var>SteamId</var> object
* of the user
* @return GameLeaderboardEntry The entry of the user if available
*/
public function getEntryForSteamId($steamId) {
if(is_object($steamId)) {
$id = $steamId->getSteamId64();
} else {
$id = $steamId;
}
$fullurl = sprintf('%s&steamid=%s', $this->url, $id);
$xml = new SimpleXMLElement(file_get_contents($fullurl));
if(!empty($xml->error)) {
throw new SteamCondenserException((string) $xml->error);
}
foreach($xml->entries->entry as $entryData) {
if($entryData->steamid == $id) {
return new GameLeaderboardEntry($entryData, $this);;
}
}
return null;
}
/**
* Returns an array of entries on this leaderboard for the user with the
* given SteamID and his/her friends
*
* @param mixed $steamId The 64bit SteamID or the <var>SteamId</var> object
* of the user
* @return array The entries of the user and his/her friends
*/
public function getEntryForSteamIdFriends($steamId) {
if(is_object($steamId)) {
$id = $steamId->getSteamId64();
} else {
$id = $steamId;
}
$fullurl = sprintf('%s&steamid=%s', $this->url, $id);
$xml = new SimpleXMLElement(file_get_contents($fullurl));
if(!empty($xml->error)) {
throw new SteamCondenserException((string) $xml->error);
}
$entries = array();
foreach($xml->entries->entry as $entryData) {
$rank = (int) $entryData->rank;
$entries[$rank] = new GameLeaderboardEntry($entryData, $this);
}
return $entries;
}
/**
* Returns the entries on this leaderboard for a given rank range
*
* The range is inclusive and a maximum of 5001 entries can be returned in
* a single request.
*
* @param int $first The first entry to return from the leaderboard
* @param int $last The last entry to return from the leaderboard
* @return array The entries that match the given rank range
*/
public function getEntryRange($first, $last) {
if($last < $first) {
throw new SteamCondenserException('First entry must be prior to last entry for leaderboard entry lookup.');
}
if(($last - $first) > 5000) {
throw new SteamCondenserException('Leaderboard entry lookup is currently limited to a maximum of 5001 entries per request.');
}
$fullurl = sprintf('%s&start=%d&end=%d', $this->url, $first, $last);
$xml = new SimpleXMLElement(file_get_contents($fullurl));
if(!empty($xml->error)) {
throw new SteamCondenserException((string) $xml->error);
}
$entries = array();
foreach($xml->entries->entry as $entryData) {
$rank = (int) $entryData->rank;
$entries[$rank] = new GameLeaderboardEntry($entryData, $this);
}
return $entries;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2011, Nicholas Hastings
* 2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
/**
* The GameLeaderboard class represents a single entry in a leaderboard
*
* @author Nicholas Hastings
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class GameLeaderboardEntry {
/**
* @var SteamId
*/
protected $steamId;
/**
* @var int
*/
protected $score;
/**
* @var int
*/
protected $rank;
/**
* @var GameLeaderboard
*/
protected $leaderboard;
/**
* Creates new entry instance for the given XML data and leaderboard
*
* @param SimpleXMLElement $entryData The XML data of the leaderboard of
* the leaderboard entry
* @param GameLeaderboard $leaderboard The leaderboard this entry belongs
* to
*/
public function __construct(SimpleXMLElement $entryData, GameLeaderboard $leaderboard) {
$this->steamId = SteamId::create((string) $entryData->steamid, false);
$this->score = (int) $entryData->score;
$this->rank = (int) $entryData->rank;
$this->leaderboard = $leaderboard;
}
/**
* Returns the Steam ID of this entry's player
*
* @return SteamId The Steam ID of the player
*/
public function getSteamId() {
return $this->steamId;
}
/**
* Returns the score of this entry
*
* @return int The score of this player
*/
public function getScore() {
return $this->score;
}
/**
* Returns the rank where this entry is listed in the leaderboard
*
* @return int The rank of this entry
*/
public function getRank() {
return $this->rank;
}
/**
* Returns the leaderboard this entry belongs to
*
* @return GameLeaderboard The leaderboard of this entry
*/
public function getLeaderboard() {
return $this->leaderboard;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2012, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/GameAchievement.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/GameLeaderboard.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/XMLData.php';
/**
* This class represents the game statistics for a single user and a specific
* game
*
* It is subclassed for individual games if the games provide special
* statistics that are unique to this game.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class GameStats extends XMLData {
/**
* @var array
*/
protected $achievements;
/**
* @var int
*/
protected $achievementsDone;
/**
* @var SteamGame
*/
protected $game;
/**
* @var SteamId
*/
protected $user;
/**
* Used to cache the XML data of the statistics for this game and this
* user
*
* @var SimpleXMLElement
*/
protected $xmlData;
/**
* Creates a <var>GameStats</var> (or one of its subclasses) instance for
* the given user and game
*
* @param string $steamId The custom URL or the 64bit Steam ID of the user
* @param string $gameName The friendly name of the game
* @return GameStats The game stats object for the given user and game
*/
public static function createGameStats($steamId, $gameName) {
switch($gameName) {
case 'alienswarm':
require_once STEAM_CONDENSER_PATH . 'steam/community/alien_swarm/AlienSwarmStats.php';
return new AlienSwarmStats($steamId);
case 'cs:s':
require_once STEAM_CONDENSER_PATH . 'steam/community/css/CSSStats.php';
return new CSSStats($steamId);
case 'defensegrid:awakening':
require_once STEAM_CONDENSER_PATH . 'steam/community/defense_grid/DefenseGridStats.php';
return new DefenseGridStats($steamId);
case 'dod:s':
require_once STEAM_CONDENSER_PATH . 'steam/community/dods/DoDSStats.php';
return new DoDSStats($steamId);
case 'l4d':
require_once STEAM_CONDENSER_PATH . 'steam/community/l4d/L4DStats.php';
return new L4DStats($steamId);
case 'l4d2':
require_once STEAM_CONDENSER_PATH . 'steam/community/l4d/L4D2Stats.php';
return new L4D2Stats($steamId);
case 'portal2':
require_once STEAM_CONDENSER_PATH . 'steam/community/portal2/Portal2Stats.php';
return new Portal2Stats($steamId);
case 'tf2':
require_once STEAM_CONDENSER_PATH . 'steam/community/tf2/TF2Stats.php';
return new TF2Stats($steamId);
default:
return new GameStats($steamId, $gameName);
}
}
/**
* Returns the base Steam Community URL for the given player and game IDs
*
* @param string $userId The 64bit SteamID or custom URL of the user
* @param mixed $gameId The application ID or short name of the game
* @return string The base URL used for the given stats IDs
*/
protected static function _getBaseUrl($userId, $gameId) {
$gameUrl = is_numeric($gameId) ? "appid/$gameId" : $gameId;
if(is_numeric($userId)) {
return "http://steamcommunity.com/profiles/$userId/stats/$gameUrl";
} else {
return "http://steamcommunity.com/id/$userId/stats/$gameUrl";
}
}
/**
* Creates a <var>GameStats</var> object and fetches data from the Steam
* Community for the given user and game
*
* @param string $steamId The custom URL or the 64bit Steam ID of the user
* @param string $gameId The app ID or friendly name of the game
* @throws SteamCondenserException if the stats cannot be fetched
*/
protected function __construct($steamId, $gameId) {
$this->user = SteamId::create($steamId, false);
$url = self::_getBaseUrl($steamId, $gameId) . '?xml=all';
$this->xmlData = $this->getData($url);
if($this->xmlData->error != null && !empty($this->xmlData->error)) {
throw new SteamCondenserException((string) $this->xmlData->error);
}
$this->privacyState = (string) $this->xmlData->privacyState;
if($this->isPublic()) {
preg_match('#http://steamcommunity.com/+app/+([1-9][0-9]*)#', (string) $this->xmlData->game->gameLink, $appId);
$this->game = SteamGame::create((int) $appId[1], $this->xmlData->game);
$this->hoursPlayed = (string) $this->xmlData->stats->hoursPlayed;
}
}
/**
* Returns the achievements for this stats' user and game
*
* If the achievements' data hasn't been parsed yet, parsing is done now.
*
* @return array All achievements belonging to this game
*/
public function getAchievements() {
if(!$this->isPublic()) {
return null;
}
if(empty($this->achievements)) {
$this->achievementsDone = 0;
foreach($this->xmlData->achievements->children() as $achievementData) {
$this->achievements[] = new GameAchievement($this->user, $this->game, $achievementData);
if((int) $achievementData->attributes()->closed) {
$this->achievementsDone += 1;
}
}
}
return $this->achievements;
}
/**
* Returns the number of achievements done by this player
*
* If achievements haven't been parsed yet for this player and this game,
* parsing is done now.
*
* @return int The number of achievements completed
* @see getAchievements()
*/
public function getAchievementsDone() {
if(empty($this->achievements)) {
$this->getAchievements();
}
return $this->achievementsDone;
}
/**
* Returns the percentage of achievements done by this player
* <p>
* If achievements haven't been parsed yet for this player and this game,
* parsing is done now.
*
* @return float The percentage of achievements completed
* @see #getAchievementsDone
*/
public function getAchievementsPercentage() {
return $this->getAchievementsDone() / sizeof($this->achievements);
}
/**
* Returns the base Steam Community URL for the stats contained in this
* object
*
* @return string The base URL used for queries on these stats
*/
public function getBaseUrl() {
return self::_getBaseUrl($this->user->getId(), $this->game->getId());
}
/**
* Returns the privacy setting of the Steam ID profile
*
* @return string The privacy setting of the Steam ID
*/
public function getPrivacyState() {
return $this->privacyState;
}
/**
* Returns the game these stats belong to
*
* @return SteamGame The game object
*/
public function getGame() {
return $this->game;
}
/**
* Returns the number of hours this game has been played by the player
*
* @return string The number of hours this game has been played
*/
public function getHoursPlayed() {
return $this->hoursPlayed;
}
/**
* Returns whether this Steam ID is publicly accessible
*
* @return bool <var>true</var> if this Steam ID is publicly accessible
*/
public function isPublic() {
return $this->privacyState == 'public';
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2009-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
/**
* An abstract class implementing basic functionality for classes representing
* game weapons
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
abstract class GameWeapon {
protected $kills;
protected $id;
protected $shots;
/**
* Creates a new game weapon instance with the data provided
*
* @param SimpleXMLElement $weaponData The data representing this weapon
*/
public function __construct(SimpleXMLElement $weaponData) {
$this->kills = (int) $weaponData->kills;
}
/**
* Returns the average number of shots needed for a kill with this weapon
*
* @return float The average number of shots needed for a kill
*/
public function getAvgShotsPerKill() {
return $this->shots / $this->kills;
}
/**
* Returns the unique identifier for this weapon
*
* @return int The identifier of this weapon
*/
public function getId() {
return $this->id;
}
/**
* Returns the number of kills achieved with this weapon
*
* @return int The number of kills achieved
*/
public function getKills() {
return $this->kills;
}
/**
* Returns the number of shots fired with this weapon
*
* @return int The number of shots fired
*/
public function getShots() {
return $this->shots;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2009-2012, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/GameStats.php';
/**
* This abstract class is a base class for statistics for Left4Dead and
* Left4Dead 2. As both games have more or less the same statistics available
* in the Steam Community the code for both is pretty much the same.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
abstract class AbstractL4DStats extends GameStats {
/**
* @var array The names of the special infected in Left4Dead
*/
protected static $SPECIAL_INFECTED = array('boomer', 'hunter', 'smoker', 'tank');
/**
* @var array
*/
protected $favorites;
/**
* @var array
*/
protected $lifetimeStats;
/**
* @var array
*/
protected $mostRecentGame;
/**
* @var array
*/
protected $survivalStats;
/**
* @var array
*/
protected $teamplayStats;
/**
* @var array
*/
protected $versusStats;
/**
* @var array
*/
protected $weaponStats;
/**
* Creates a new instance of statistics for both, Left4Dead and Left4Dead 2
* parsing basic common data
*
* @param string $steamId The custom URL or 64bit Steam ID of the user
* @param string $gameName The name of the game
*/
public function __construct($steamId, $gameName) {
parent::__construct($steamId, $gameName);
if($this->isPublic() && !empty($this->xmlData->stats->mostrecentgame)) {
$this->mostRecentGame['difficulty'] = (string) $this->xmlData->stats->mostrecentgame->difficulty;
$this->mostRecentGame['escaped'] = (bool) $this->xmlData->stats->mostrecentgame->bEscaped;
$this->mostRecentGame['movie'] = (string) $this->xmlData->stats->mostrecentgame->movie;
$this->mostRecentGame['timePlayed'] = (string) $this->xmlData->stats->mostrecentgame->time;
}
}
/**
* Returns an array of favorites for this user like weapons and character
*
* If the favorites haven't been parsed already, parsing is done now.
*
* @return array The favorites of this user
*/
public function getFavorites() {
if(!$this->isPublic()) {
return null;
}
if(empty($this->favorites)) {
$this->favorites = array();
$this->favorites['campaign'] = (string) $this->xmlData->stats->favorites->campaign;
$this->favorites['campaignPercentage'] = (int) $this->xmlData->stats->favorites->campaignpct;
$this->favorites['character'] = (string) $this->xmlData->stats->favorites->character;
$this->favorites['characterPercentage'] = (int) $this->xmlData->stats->favorites->characterpct;
$this->favorites['level1Weapon'] = (string) $this->xmlData->stats->favorites->weapon1;
$this->favorites['level1Weapon1Percentage'] = (int) $this->xmlData->stats->favorites->weapon1pct;
$this->favorites['level2Weapon'] = (string) $this->xmlData->stats->favorites->weapon2;
$this->favorites['level2Weapon1Percentage'] = (int) $this->xmlData->stats->favorites->weapon2pct;
}
return $this->favorites;
}
/**
* Returns an array of lifetime statistics for this user like the time
* played
*
* If the lifetime statistics haven't been parsed already, parsing is done
* now.
*
* @return array The lifetime statistics for this user
*/
public function getLifetimeStats() {
if(!$this->isPublic()) {
return null;
}
if(empty($this->lifetimeStats)) {
$this->lifetimeStats = array();
$this->lifetimeStats['finalesSurvived'] = (int) $this->xmlData->stats->lifetime->finales;
$this->lifetimeStats['gamesPlayed'] = (int) $this->xmlData->stats->lifetime->gamesplayed;
$this->lifetimeStats['finalesSurvivedPercentage'] = $this->lifetimeStats['finalesSurvived'] / $this->lifetimeStats['gamesPlayed'];
$this->lifetimeStats['infectedKilled'] = (int) $this->xmlData->stats->lifetime->infectedkilled;
$this->lifetimeStats['killsPerHour'] = (float) $this->xmlData->stats->lifetime->killsperhour;
$this->lifetimeStats['avgKitsShared'] = (float) $this->xmlData->stats->lifetime->kitsshared;
$this->lifetimeStats['avgKitsUsed'] = (float) $this->xmlData->stats->lifetime->kitsused;
$this->lifetimeStats['avgPillsShared'] = (float) $this->xmlData->stats->lifetime->pillsshared;
$this->lifetimeStats['avgPillsUsed'] = (float) $this->xmlData->stats->lifetime->pillused;
$this->lifetimeStats['timePlayed'] = (string) $this->xmlData->stats->lifetime->timeplayed;
}
return $this->lifetimeStats;
}
/**
* Returns an array of Survival statistics for this user like revived
* teammates
*
* If the Survival statistics haven't been parsed already, parsing is done
* now.
*
* @return array The Survival statistics for this user
*/
public function getSurvivalStats() {
if(!$this->isPublic()) {
return null;
}
if(empty($this->survivalStats)) {
$this->survivalStats = array();
$this->survivalStats['goldMedals'] = (int) $this->xmlData->stats->survival->goldmedals;
$this->survivalStats['silverMedals'] = (int) $this->xmlData->stats->survival->silvermedals;
$this->survivalStats['bronzeMedals'] = (int) $this->xmlData->stats->survival->bronzemedals;
$this->survivalStats['roundsPlayed'] = (int) $this->xmlData->stats->survival->roundsplayed;
$this->survivalStats['bestTime'] = (float) $this->xmlData->stats->survival->besttime;
}
return $this->survivalStats;
}
/**
* Returns an array of teamplay statistics for this user like revived
* teammates
*
* If the teamplay statistics haven't been parsed already, parsing is done
* now.
*
* @return array The teamplay statistics for this
*/
public function getTeamplayStats() {
if(!$this->isPublic()) {
return null;
}
if(empty($this->teamplayStats)) {
$this->teamplayStats = array();
$this->teamplayStats['revived'] = (int) $this->xmlData->stats->teamplay->revived;
$this->teamplayStats['mostRevivedDifficulty'] = (string) $this->xmlData->stats->teamplay->reviveddiff;
$this->teamplayStats['avgRevived'] = (float) $this->xmlData->stats->teamplay->revivedavg;
$this->teamplayStats['avgWasRevived'] = (float) $this->xmlData->stats->teamplay->wasrevivedavg;
$this->teamplayStats['protected'] = (int) $this->xmlData->stats->teamplay->protected;
$this->teamplayStats['mostProtectedDifficulty'] = (string) $this->xmlData->stats->teamplay->protecteddiff;
$this->teamplayStats['avgProtected'] = (float) $this->xmlData->stats->teamplay->protectedavg;
$this->teamplayStats['avgWasProtected'] = (float) $this->xmlData->stats->teamplay->wasprotectedavg;
$this->teamplayStats['friendlyFireDamage'] = (int) $this->xmlData->stats->teamplay->ffdamage;
$this->teamplayStats['mostFriendlyFireDifficulty'] = (string) $this->xmlData->stats->teamplay->ffdamagediff;
$this->teamplayStats['avgFriendlyFireDamage'] = (float) $this->xmlData->stats->teamplay->ffdamageavg;
}
return $this->teamplayStats;
}
/**
* Returns an array of Versus statistics for this user like percentage of
* rounds won
*
* If the Versus statistics haven't been parsed already, parsing is done
* now.
*
* @return array The Versus statistics for this user
*/
public function getVersusStats() {
if(!$this->isPublic()) {
return null;
}
if(empty($this->versusStats)) {
$this->versusStats = array();
$this->versusStats['gamesPlayed'] = (int) $this->xmlData->stats->versus->gamesplayed;
$this->versusStats['gamesCompleted'] = (int) $this->xmlData->stats->versus->gamescompleted;
$this->versusStats['finalesSurvived'] = (int) $this->xmlData->stats->versus->finales;
$this->versusStats['finalesSurvivedPercentage'] = ($this->versusStats['gamesPlayed']) ? $this->versusStats['finalesSurvived'] / $this->versusStats['gamesPlayed'] : 0;
$this->versusStats['points'] = (int) $this->xmlData->stats->versus->points;
$this->versusStats['mostPointsInfected'] = (string) $this->xmlData->stats->versus->pointas;
$this->versusStats['gamesWon'] = (int) $this->xmlData->stats->versus->gameswon;
$this->versusStats['gamesLost'] = (int) $this->xmlData->stats->versus->gameslost;
$this->versusStats['highestSurvivorScore'] = (int) $this->xmlData->stats->versus->survivorscore;
foreach($this->SPECIAL_INFECTED() as $infected) {
$this->versusStats[$infected] = array();
$this->versusStats[$infected]['specialAttacks'] = (int) $this->xmlData->stats->versus->{$infected . 'special'};
$this->versusStats[$infected]['mostDamage'] = (int) $this->xmlData->stats->versus->{$infected . 'dmg'};
$this->versusStats[$infected]['avgLifespan'] = (float) $this->xmlData->stats->versus->{$infected . 'lifespan'};
}
}
return $this->versusStats;
}
/**
* Returns the names of the special infected in Left4Dead
*
* Hacky workaround for PHP not allowing arrays as class constants
*
* @return array The names of the special infected in Left4Dead
*/
protected function SPECIAL_INFECTED() {
return self::$SPECIAL_INFECTED;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2009-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/GameWeapon.php';
/**
* This abstract class is a base class for weapons in Left4Dead and Left4Dead 2
* as the weapon stats for both games are very similar
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
abstract class AbstractL4DWeapon extends GameWeapon {
/**
* @var string
*/
protected $accuracy;
/**
* @var string
*/
protected $headshotsPercentage;
/**
* @var string
*/
protected $killPercentage;
/**
* @var string
*/
protected $name;
/**
* @var string
*/
protected $shots;
/**
* Creates a new instance of weapon from the given XML data and parses
* common data for both, <var>L4DWeapon</var> and <var>L4D2Weapon</var>
*
* @param SimpleXMLElement $weaponData The XML data for this weapon
*/
public function __construct(SimpleXMLElement $weaponData) {
parent::__construct($weaponData);
$this->accuracy = ((float) $weaponData->accuracy) * 0.01;
$this->headshotsPercentage = ((float) $weaponData->headshots) * 0.01;
$this->id = $weaponData->getName();
$this->shots = (int) $weaponData->shots;
}
/**
* Returns the overall accuracy of the player with this weapon
*
* @return string The accuracy of the player with this weapon
*/
public function getAccuracy() {
return $this->accuracy;
}
/**
* Returns the percentage of kills with this weapon that have been
* headshots
*
* @return string The percentage of headshots with this weapon
*/
public function getHeadshotsPercentage() {
return $this->headshotsPercentage;
}
/**
* Returns the ID of the weapon
*
* @return string The ID of the weapon
*/
public function getId() {
return $this->id;
}
/**
* Returns the percentage of overall kills of the player that have been
* achieved with this weapon
*
* @return string The percentage of kills with this weapon
*/
public function getKillPercentage() {
return $this->killPercentage;
}
/**
* Returns the number of shots the player has fired with this weapon
*
* @return int The number of shots with this weapon
*/
public function getShots() {
return $this->shots;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2009-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/SteamId.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/l4d/L4DMap.php';
/**
* This class holds statistical information about a map played by a player in
* Survival mode of Left4Dead 2
*
* The basic information provided is more or less the same for Left4Dead and
* Left4Dead 2, but parsing has to be done differently.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class L4D2Map extends L4DMap {
/**
* @var array The names of the special infected in Left4Dead 2
*/
protected static $SPECIAL_INFECTED = array('boomer', 'charger', 'hunter', 'jockey', 'smoker', 'spitter', 'tank');
/**
* @var array The items available in Left4Dead 2
*/
private static $ITEMS = array('adrenaline', 'defibs', 'medkits', 'pills');
/**
* @var array
*/
private $items;
/**
* @var array
*/
private $kills;
/**
* @var bool
*/
private $played;
/**
* @var array
*/
private $teammates;
/**
* Creates a new instance of a map based on the given XML data
*
* The map statistics for the Survival mode of Left4Dead 2 hold much more
* information than those for Left4Dead, e.g. the teammates and items are
* listed.
*
* @param SimpleXMLElement $mapData The XML data for this map
*/
public function __construct(SimpleXMLElement $mapData) {
$this->bestTime = (float) $mapData->besttimeseconds;
preg_match('#http://steamcommunity.com/public/images/gamestats/550/(.*)\.jpg#', (string) $mapData->img, $id);
$this->id = $id[1];
$this->name = (string) $mapData->name;
$this->played = ((int) $mapData->hasPlayed == 1);
if($this->played) {
$this->bestTime = (float) $mapData->besttimemilliseconds / 1000;
$this->teammates = array();
foreach($mapData->teammates->children() as $teammate) {
$this->teammates[] = new SteamId((string) $teammate, false);
}
$this->items = array();
foreach(self::$ITEMS as $item) {
$this->items[$item] = (int) $mapData->{"item_$item"};
}
$this->kills = array();
foreach(self::$INFECTED as $infected) {
$this->kills[$infected] = (int) $mapData->{"kills_$infected"};
}
switch((string) $mapData->medal) {
case 'gold':
$this->medal = self::GOLD;
break;
case 'silver':
$this->medal = self::SILVER;
break;
case 'bronze':
$this->medal = self::BRONZE;
break;
default:
$this->medal = self::NONE;
}
}
}
/**
* Returns statistics about the items used by the player on this map
*
* @return array The items used by the player
*/
public function getItems() {
return $this->items;
}
/**
* Returns the number of special infected killed by the player grouped by
* the names of the special infected
*
* @return array The special infected killed by the player
*/
public function getKills() {
return $this->kills;
}
/**
* Returns the SteamIDs of the teammates of the player in his best game on
* this map
*
* @return array The SteamIDs of the teammates in the best game
*/
public function getTeammates() {
return $this->teammates;
}
/**
* Returns whether the player has already played this map
*
* @return bool <var>true</var> if the player has already played this map
*/
public function isPlayed() {
return $this->played;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2009-2012, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/l4d/AbstractL4DStats.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/l4d/L4D2Map.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/l4d/L4D2Weapon.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/l4d/L4DExplosive.php';
/**
* This class represents the game statistics for a single user in Left4Dead 2
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class L4D2Stats extends AbstractL4DStats {
/**
* @var array The names of the special infected in Left4Dead 2
*/
protected static $SPECIAL_INFECTED = array('boomer', 'charger', 'hunter', 'jockey', 'smoker', 'spitter', 'tank');
/**
* @var array
*/
private $damagePercentages;
/**
* @var array
*/
private $scavengeStats;
/**
* Creates a <var>L4D2Stats</var> object by calling the super constructor
* with the game name <var>"l4d2"</var>
*
* @param string $steamId The custom URL or 64bit Steam ID of the user
*/
public function __construct($steamId) {
parent::__construct($steamId, 'l4d2');
$this->damagePercentages = array(
'melee' => (float) $this->xmlData->stats->weapons->meleePctDmg,
'pistols' => (float) $this->xmlData->stats->weapons->pistolsPctDmg,
'rifles' => (float) $this->xmlData->stats->weapons->bulletsPctDmg,
'shotguns' => (float) $this->xmlData->stats->weapons->shellsPctDmg
);
}
/**
* Returns an array of lifetime statistics for this user like the time
* played
*
* If the lifetime statistics haven't been parsed already, parsing is done
* now.
*
* There are only a few additional lifetime statistics for Left4Dead 2
* which are not generated for Left4Dead, so this calls
* <var>AbstractL4DStats#getLifetimeStats()</var> first and adds some
* additional stats.
*
* @return array The lifetime statistics of the player in Left4Dead 2
*/
public function getLifetimeStats() {
if(!$this->isPublic()) {
return null;
}
if(empty($this->lifetimeStats)) {
parent::getLifetimeStats();
$this->lifetimeStats['avgAdrenalineShared'] = (float) $this->xmlData->stats->lifetime->adrenalineshared;
$this->lifetimeStats['avgAdrenalineUsed'] = (float) $this->xmlData->stats->lifetime->adrenalineused;
$this->lifetimeStats['avgDefibrillatorsUsed'] = (float) $this->xmlData->stats->lifetime->defibrillatorsused;
}
return $this->lifetimeStats;
}
/**
* Returns the percentage of damage done by this player with each weapon
* type
*
* Available weapon types are <var>"melee"</var>, <var>"pistols"</var>,
* <var>"rifles"</var> and <var>"shotguns"</var>.
*
* @return float The percentages of damage done with each weapon type
*/
public function getDamagePercentage() {
return $this->DamagePercentage;
}
/**
* Returns the percentage of damage done by this player with pistols
*
* @return float The percentage of damage done with pistols
*/
public function getPistolDamagePercentage() {
return $this->pistolDamagePercentage;
}
/**
* Returns the percentage of damage done by this player with rifles
*
* @return float The percentage of damage done with rifles
*/
public function getRifleDamagePercentage() {
return $this->rifleDamagePercentage;
}
/**
* Returns an array of Scavenge statistics for this user like the number of
* Scavenge rounds played
*
* If the Scavenge statistics haven't been parsed already, parsing is done
* now.
*
* @return array The Scavenge statistics of the player
*/
public function getScavengeStats() {
if(!$this->isPublic()) {
return null;
}
if(empty($this->scavengeStats)) {
$this->scavengeStats = array();
$this->scavengeStats['avgCansPerRound'] = (float) $this->xmlData->stats->scavenge->avgcansperround;
$this->scavengeStats['perfectRounds'] = (int) $this->xmlData->stats->scavenge->perfect16canrounds;
$this->scavengeStats['roundsLost'] = (int) $this->xmlData->stats->scavenge->roundslost;
$this->scavengeStats['roundsPlayed'] = (int) $this->xmlData->stats->scavenge->roundsplayed;
$this->scavengeStats['roundsWon'] = (int) $this->xmlData->stats->scavenge->roundswon;
$this->scavengeStats['totalCans'] = (int) $this->xmlData->stats->scavenge->totalcans;
$this->scavengeStats['maps'] = array();
foreach($this->xmlData->stats->scavenge->mapstats->children() as $mapData) {
$map_id = (string) $mapData->name;
$this->scavengeStats['maps'][$map_id] = array();
$this->scavengeStats['maps'][$map_id]['avgRoundScore'] = (int) $mapData->avgscoreperround;
$this->scavengeStats['maps'][$map_id]['highestGameScore'] = (int) $mapData->highgamescore;
$this->scavengeStats['maps'][$map_id]['highestRoundScore'] = (int) $mapData->avgscoreperround;
$this->scavengeStats['maps'][$map_id]['name'] = (string) $mapData->fullname;
$this->scavengeStats['maps'][$map_id]['roundsPlayed'] = (int) $mapData->roundsplayed;
$this->scavengeStats['maps'][$map_id]['roundsWon'] = (int) $mapData->roundswon;
}
$this->scavengeStats['infected'] = array();
foreach($this->xmlData->stats->scavenge->infectedstats->children() as $infectedData) {
$infectedId = (string) $infectedData->name;
$this->scavengeStats['infected'][$infectedId] = array();
$this->scavengeStats['infected'][$infectedId]['maxDamagePerLife'] = (int) $infectedData->maxdmg1life;
$this->scavengeStats['infected'][$infectedId]['maxPoursInterrupted'] = (int) $infectedData->maxpoursinterrupted;
$this->scavengeStats['infected'][$infectedId]['specialAttacks'] = (int) $infectedData->specialattacks;
}
}
return $this->scavengeStats;
}
/**
* Returns the percentage of damage done by this player with shotguns
*
* @return float The percentage of damage done with shotguns
*/
public function getShotgunDamagePercentage() {
return $this->shotgunDamagePercentage;
}
/**
* Returns an array of Survival statistics for this user like revived
* teammates
*
* If the Survival statistics haven't been parsed already, parsing is done
* now.
*
* The XML layout for the Survival statistics for Left4Dead 2 differs a bit
* from Left4Dead's Survival statistics. So we have to use a different way
* of parsing for the maps and we use a different map class
* (<var>L4D2Map</var>) which holds the additional information provided in
* Left4Dead 2's statistics.
*
* @return array The Survival statistics of the player
*/
public function getSurvivalStats() {
if(!$this->isPublic()) {
return null;
}
if(empty($this->survivalStats)) {
parent::getSurvivalStats();
$this->survivalStats['maps'] = array();
foreach($this->xmlData->stats->survival->maps->children() as $mapData) {
$map = new L4D2Map($mapData);
$this->survivalStats['maps'][$map->getId()] = $map;
}
}
return $this->survivalStats;
}
/**
* Returns an array of <var>L4D2Weapon</var> for this user containing all
* Left4Dead 2 weapons
*
* If the weapons haven't been parsed already, parsing is done now.
*
* @return array The weapon statistics for this player
*/
public function getWeaponStats() {
if(!$this->isPublic()) {
return null;
}
if(empty($this->weaponStats)) {
$this->weaponStats = array();
foreach($this->xmlData->stats->weapons->children() as $weaponData) {
if(empty($weaponData)) {
continue;
}
$weaponName = $weaponData->getName();
if(!in_array($weaponName, array('bilejars', 'molotov', 'pipes'))) {
$weapon = new L4D2Weapon($weaponData);
}
else {
$weapon = new L4DExplosive($weaponData);
}
$this->weaponStats[$weaponName] = $weapon;
}
}
return $this->weaponStats;
}
/**
* Returns the names of the special infected in Left4Dead 2
*
* Hacky workaround for PHP not allowing arrays as class constants
*
* @return array The names of the special infected in Left4Dead 2
*/
protected function SPECIAL_INFECTED() {
return self::$SPECIAL_INFECTED;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2009-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/l4d/AbstractL4DWeapon.php';
/**
* This class represents the statistics of a single weapon for a user in
* Left4Dead 2
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class L4D2Weapon extends AbstractL4DWeapon {
/**
* @var int
*/
private $damage;
/**
* @var string
*/
private $weaponGroup;
/**
* Creates a new instance of a weapon based on the given XML data
*
* @param SimpleXMLElement $weaponData The XML data of this weapon
*/
public function __construct(SimpleXMLElement $weaponData) {
parent::__construct($weaponData);
$this->damage = (int) $weaponData->damage;
$this->killPercentage = ((float) $weaponData->pctkills) * 0.01;
$this->weaponGroup = $weaponData['group'];
}
/**
* Returns the amount of damage done by the player with this weapon
*
* @return int The damage done by this weapon
*/
public function getDamage() {
return $this->damage;
}
/**
* Returns the weapon group this weapon belongs to
*
* @return string The group this weapon belongs to
*/
public function getWeaponGroup() {
return $this->weaponGroup;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2009-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/GameWeapon.php';
/**
* This class represents the statistics of a single explosive weapon for a user
* in Left4Dead
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class L4DExplosive extends GameWeapon {
/**
* Creates a new instance of an explosivve based on the given XML data
*
* @param SimpleXMLElement $weaponData The XML data of this explosive
*/
public function __construct(SimpleXMLElement $weaponData) {
parent::__construct($weaponData);
$this->id = $weaponData->getName();
$this->shots = (int) $weaponData->thrown;
}
/**
* Returns the average number of killed zombies for one shot of this
* explosive
*
* @return float The average number of kills per shot
*/
public function getAvgKillsPerShot() {
return 1 / $this->getAvgShotsPerKill();
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2009-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
/**
* This class holds statistical information about a map played by a player in
* Survival mode of Left4Dead
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class L4DMap {
const GOLD = 1;
const SILVER = 2;
const BRONZE = 3;
const NONE = 0;
protected $bestTime;
protected $id;
protected $medal;
protected $name;
private $timesPlayed;
/**
* Creates a new instance of a Left4Dead Survival map based on the given
* XML data
*
* @param SimpleXMLElement $mapData The XML data for this map
*/
public function __construct(SimpleXMLElement $mapData) {
$this->bestTime = (float) $mapData->besttimeseconds;
$this->id = $mapData->getName();
$this->name = (string) $mapData->name;
$this->timesPlayed = (int) $mapData->timesplayed;
switch((string) $mapData->medal) {
case 'gold':
$this->medal = self::GOLD;
break;
case 'silver':
$this->medal = self::SILVER;
break;
case 'bronze':
$this->medal = self::BRONZE;
break;
default:
$this->medal = self::NONE;
}
}
/**
* Returns the best survival time of this player on this map
*
* @return float The best survival time of this player on this map
*/
public function getBestTime() {
return $this->bestTime;
}
/**
* Returns the ID of this map
*
* @return string The ID of this map
*/
public function getId() {
return $this->id;
}
/**
* Returns the highest medal this player has won on this map
*
* @return int The highest medal won by this player on this map
*/
public function getMedal() {
return $this->medal;
}
/**
* Returns the name of the map
*
* @return string The name of the map
*/
public function getName() {
return $this->name;
}
/**
* Returns the number of times this map has been played by this player
*
* @return int The number of times this map has been played
*/
public function getTimesPlayed() {
return $this->timesPlayed;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2009-2012, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/l4d/AbstractL4DStats.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/l4d/L4DExplosive.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/l4d/L4DMap.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/l4d/L4DWeapon.php';
/**
* This class represents the game statistics for a single user in Left4Dead
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class L4DStats extends AbstractL4DStats {
/**
* Creates a <var>L4DStats</var> object by calling the super constructor
* with the game name <var>"l4d"</var>
*
* @param string $steamId The custom URL or 64bit Steam ID of the user
*/
public function __construct($steamId) {
parent::__construct($steamId, 'l4d');
}
/**
* Returns an array of Survival statistics for this user like revived
* teammates
*
* If the Survival statistics haven't been parsed already, parsing is done
* now.
*
* @return array The stats for the Survival mode
*/
public function getSurvivalStats() {
if(!$this->isPublic()) {
return null;
}
if(empty($this->survivalStats)) {
parent::getSurvivalStats();
$this->survivalStats['maps'] = array();
foreach($this->xmlData->stats->survival->maps->children() as $mapData) {
$this->survivalStats['maps'][$mapData->getName()] = new L4DMap($mapData);
}
}
return $this->survivalStats;
}
/**
* Returns an array of <var>L4DWeapon</var> for this user containing all
* Left4Dead weapons
*
* If the weapons haven't been parsed already, parsing is done now.
*
* @return array The weapon statistics
*/
public function getWeaponStats() {
if(!$this->isPublic()) {
return null;
}
if(empty($this->weaponStats)) {
$this->weaponStats = array();
foreach($this->xmlData->stats->weapons->children() as $weaponData) {
$weaponName = $weaponData->getName();
if($weaponName != 'molotov' && $weaponName != 'pipes') {
$weapon = new L4DWeapon($weaponData);
}
else {
$weapon = new L4DExplosive($weaponData);
}
$this->weaponStats[$weaponName] = $weapon;
}
}
return $this->weaponStats;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2009-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/l4d/AbstractL4DWeapon.php';
/**
* This class represents the statistics of a single weapon for a user in
* Left4Dead
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class L4DWeapon extends AbstractL4DWeapon {
/**
* Creates a new instance of a weapon based on the given XML data
*
* @param SimpleXMLElement $weaponData The XML data for this weapon
*/
public function __construct(SimpleXMLElement $weaponData) {
parent::__construct($weaponData);
$this->killPercentage = ((float) $weaponData->killpct) * 0.01;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2011-2012, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/GameInventory.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/portal2/Portal2Item.php';
/**
* Represents the inventory (aka. Robot Enrichment) of a Portal 2 player
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class Portal2Inventory extends GameInventory {
const APP_ID = 620;
const ITEM_CLASS = 'Portal2Item';
/**
* This checks the cache for an existing inventory. If it exists it is
* returned. Otherwise a new inventory is created.
*
* @param string $steamId The 64bit Steam ID or vanity URL of the user
* @param bool $fetchNow Whether the data should be fetched now
* @param bool $bypassCache Whether the cache should be bypassed
* @return Portal2Inventory The inventory created from the given options
*/
public static function createInventory($steamId, $fetchNow = true, $bypassCache = false) {
return parent::create(self::APP_ID, $steamId, $fetchNow, $bypassCache);
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/GameItem.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/portal2/Portal2Inventory.php';
/**
* Represents a Portal 2 item
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class Portal2Item extends GameItem {
/**
* @var array The names of the bots available in Portal 2
*/
private static $BOTS = array('pbody', 'atlas');
/**
* @var array
*/
private $equipped;
/**
* Creates a new instance of a Portal2Item with the given data
*
* @param Portal2Inventory $inventory The inventory this item is contained
* in
* @param array $itemData The data specifying this item
* @throws WebApiException on Web API errors
*/
public function __construct(Portal2Inventory $inventory, $itemData) {
parent::__construct($inventory, $itemData);
$this->equipped = array();
for($botId = 0; $botId < sizeof(self::$BOTS); $botId++) {
$this->equipped[self::$BOTS[$botId]] = (($itemData->inventory & (1 << 16 + $botId)) != 0);
}
}
/**
* Returns the name for each bot this player has equipped this item
*
* @return array The names of the bots this player has equipped this item
*/
public function getBotsEquipped() {
$botsEquipped = array();
foreach($this->equipped as $botId => $equipped) {
if($equipped) {
$botsEquipped[] = $botId;
}
}
return $botsEquipped;
}
/**
* Returns whether this item is equipped by this player at all
*
* @return bool Whether this item is equipped by this player at all
*/
public function isEquipped() {
return in_array(true, $this->equipped);
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2011-2013, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/GameStats.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/portal2/Portal2Inventory.php';
/**
* This class represents the game statistics for a single user in Portal 2
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class Portal2Stats extends GameStats {
/**
* @var Portal2Inventory
*/
private $inventory;
/**
* Creates a <var>Portal2Stats</var> object by calling the super
* constructor with the game name <var>"portal2"</var>
*
* @param string $steamId The custom URL or 64bit Steam ID of the user
*/
public function __construct($steamId) {
parent::__construct($steamId, 'portal2');
}
/**
* Returns the current Portal 2 inventory (a.k.a. Robot Enrichment) of this
* player
*
* @return Portal2Inventory This player's Portal 2 backpack
*/
public function getInventory() {
if(!$this->isPublic()) {
return null;
}
if(empty($this->inventory)) {
$this->inventory = Portal2Inventory::createInventory($this->user->getId());
}
return $this->inventory;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2011-2013, Sebastian Staudt
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/GameLeaderboard.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/GameStats.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/WebApi.php';
/**
* This class represents a game available on Steam
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class SteamGame {
/**
* @var array
*/
private static $games = array();
/**
* @var int
*/
private $appId;
/**
* @var string
*/
private $logoHash;
/**
* @var string
*/
private $name;
/**
* @var string
*/
private $shortName;
/**
* Creates a new or cached instance of the game specified by the given XML
* data
*
* @param int $appId The application ID of the game
* @param SimpleXMLElement $gameData The XML data of the game
* @return SteamGame The game instance for the given data
* @see __construct()
*/
public static function create($appId, SimpleXMLElement $gameData) {
if(array_key_exists($appId, self::$games)) {
return self::$games[$appId];
} else {
return new SteamGame($appId, $gameData);
}
}
/**
* Checks if a game is up-to-date by reading information from a
* <var>steam.inf</var> file and comparing it using the Web API
*
* @param string $path The file system path of the <var>steam.inf</var>
* file
* @return bool <var>true</var> if the game is up-to-date
* @throws SteamCondenserException if the <var>steam.inf</var> is invalid
*/
public static function checkSteamInf($path) {
$steamInf = file_get_contents($path);
preg_match('/^\s*appID=(\d+)\s*$/im', $steamInf, $appId);
preg_match('/^\s*PatchVersion=([\d\.]+)\s*$/im', $steamInf, $version);
if($appId == null || $version == null) {
throw new SteamCondenserException("The steam.inf file at \"$path\" is invalid.");
}
$appId = (int) $appId[1];
$version = (int) str_replace('.', '', $version[1]);
return self::checkUpToDate($appId, $version);
}
/**
* Returns whether the given version of the game with the given application
* ID is up-to-date
*
* @param int $appId The application ID of the game to check
* @param int $version The version to check against the Web API
* @return boolean <var>true</var> if the given version is up-to-date
* @throws SteamCondenserException if the Web API request fails
*/
public static function checkUpToDate($appId, $version) {
$params = array('appid' => $appId, 'version' => $version);
$result = WebApi::getJSON('ISteamApps', 'UpToDateCheck', 1, $params);
$result = json_decode($result)->response;
if(!$result->success) {
throw new SteamCondenserException($result->error);
}
return $result->up_to_date;
}
/**
* Creates a new instance of a game with the given data and caches it
*
* @param int $appId The application ID of the game
* @param SimpleXMLElement $gameData The XML data of the game
*/
private function __construct($appId, SimpleXMLElement $gameData) {
$this->appId = $appId;
if(!empty($gameData->name)) {
$logoUrl = (string) $gameData->logo;
$this->name = (string) $gameData->name;
if($gameData->globalStatsLink != null && !empty($gameData->globalStatsLink)) {
preg_match('#http://steamcommunity.com/stats/([^?/]+)/achievements/#', (string) $gameData->globalStatsLink, $shortName);
$this->shortName = strtolower($shortName[1]);
} else {
$this->shortName = null;
}
} else {
$this->iconUrl = (string) $gameData->gameIcon;
$logoUrl = (string) $gameData->gameLogo;
$this->name = (string) $gameData->gameName;
$this->shortName = strtolower((string) $gameData->gameFriendlyName);
}
preg_match("#/$appId/([0-9a-f]+).jpg#", $logoUrl, $logoHash);
if (!empty($logoHash)) {
$this->logoHash = strtolower($logoHash[1]);
}
self::$games[$appId] = $this;
}
/**
* Returns the Steam application ID of this game
*
* @return int The Steam application ID of this game
*/
public function getAppId() {
return $this->appId;
}
/**
* Returns a unique identifier for this game
*
* This is either the numeric application ID or the unique short name
*
* @return mixed The application ID or short name of the game
*/
public function getId() {
if((string) $this->appId == $this->shortName) {
return $this->appId;
} else {
return $this->shortName;
}
}
/**
* Returns the leaderboard for this game and the given leaderboard ID or
* name
*
* @param mixed $id The ID or name of the leaderboard to return
* @return GameLeaderboard The matching leaderboard if available
*/
public function getLeaderboard($id) {
return GameLeaderboard::getLeaderboard($this->shortName, $id);
}
/**
* Returns an array containing all of this game's leaderboards
*
* @return array The leaderboards for this game
*/
public function getLeaderboards() {
return GameLeaderboard::getLeaderboards($this->shortName);
}
/**
* Returns the URL for the logo image of this game
*
* @return string The URL for the game logo
*/
public function getLogoUrl() {
if ($this->logoHash == null) {
return null;
} else {
return "http://media.steampowered.com/steamcommunity/public/images/apps/{$this->appId}/{$this->logoHash}.jpg";
}
}
/**
* Returns the URL for the logo thumbnail image of this game
*
* @return string The URL for the game logo thumbnail
*/
public function getLogoThumbnailUrl() {
if ($this->logoHash == null) {
return null;
} else {
return "http://media.steampowered.com/steamcommunity/public/images/apps/{$this->appId}/{$this->logoHash}_thumb.jpg";
}
}
/**
* Returns the full name of this game
*
* @return string The full name of this game
*/
public function getName() {
return $this->name;
}
/**
* Returns the overall number of players currently playing this game
*
* @return int The number of players playing this game
*/
public function getPlayerCount() {
$params = array('appid' => $this->appId);
$result = WebApi::getJSON('ISteamUserStats', 'GetNumberOfCurrentPlayers', 1, $params);
$result = json_decode($result);
return $result->response->player_count;
}
/**
* Returns the short name of this game (also known as "friendly name")
*
* @return string|null
* The short name of this game, or null if this game does not have a
* short name.
*/
public function getShortName() {
return $this->shortName;
}
/**
* Returns the URL of this game's page in the Steam Store
*
* @return string This game's store page
*/
public function getStoreUrl() {
return "http://store.steampowered.com/app/{$this->appId}";
}
/**
* Creates a stats object for the given user and this game
*
* @param string $steamId The custom URL or the 64bit Steam ID of the user
* @return GameStats The stats of this game for the given user
*/
public function getUserStats($steamId) {
if(!$this->hasStats()) {
return null;
}
return GameStats::createGameStats($steamId, $this->shortName);
}
/**
* Returns whether this game has statistics available
*
* @return bool <var>true</var> if this game has stats
*/
public function hasStats() {
return $this->shortName != null;
}
/**
* Returns whether the given version of this game is up-to-date
*
* @param int $version The version to check against the Web API
* @return boolean <var>true</var> if the given version is up-to-date
*/
public function isUpToDate($version) {
return self::checkUpToDate($this->appId, $version);
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2012, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/SteamId.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/XMLData.php';
/**
* The SteamGroup class represents a group in the Steam Community
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class SteamGroup extends XMLData {
/**
* @var array
*/
private static $steamGroups = array();
/**
* @var String
*/
private $customUrl;
/**
* @var int
*/
private $fetchTime;
/**
* @var int
*/
private $groupId64;
/**
* @var array
*/
private $members;
/**
* Returns whether the requested group is already cached
*
* @param string $id The custom URL of the group specified by the group
* admin or the 64bit group ID
* @return bool <var>true</var> if this group is already cached
*/
public static function isCached($id) {
return array_key_exists(strtolower($id), self::$steamGroups);
}
/**
* Clears the group cache
*/
public static function clearCache() {
self::$steamGroups = array();
}
/**
* Creates a new <var>SteamGroup</var> instance or gets an existing one
* from the cache for the group with the given ID
*
* @param string $id The custom URL of the group specified by the group
* admin or the 64bit group ID
* @param bool $fetch if <var>true</var> the groups's data is loaded into
* the object
* @param bool $bypassCache If <var>true</var> an already cached instance
* for this group will be ignored and a new one will be created
* @return SteamGroup The <var>SteamGroup</var> instance of the requested
* group
*/
public static function create($id, $fetch = true, $bypassCache = false) {
$id = strtolower($id);
if(self::isCached($id) && !$bypassCache) {
$group = self::$steamGroups[$id];
if($fetch && !$group->isFetched()) {
$group->fetchMembers();
}
return $group;
} else {
return new SteamGroup($id, $fetch);
}
}
/**
* Creates a new <var>SteamGroup</var> instance for the group with the
* given ID
*
* @param string $id The custom URL of the group specified by the group
* admin or the 64bit group ID
* @param bool $fetch if <var>true</var> the groups's data is loaded into
* the object
*/
public function __construct($id, $fetch = true) {
if(is_numeric($id)) {
$this->groupId64 = $id;
} else {
$this->customUrl = $id;
}
$this->fetched = false;
$this->members = array();
if($fetch) {
$this->fetchMembers();
}
$this->cache();
}
/**
* Saves this SteamGroup in the cache
*
* @return <var>false</var> if this group is already cached
*/
public function cache() {
if(!array_key_exists($this->groupId64, self::$steamGroups)) {
self::$steamGroups[$this->groupId64] = $this;
if(!empty($this->customUrl) &&
!array_key_exists($this->customUrl, self::$steamGroups)) {
self::$steamGroups[$this->customUrl] = $this;
}
}
}
/**
* Loads the members of this group
*
* This might take several HTTP requests as the Steam Community splits this
* data over several XML documents if the group has lots of members.
*/
public function fetchMembers() {
if(empty($this->memberCount) || sizeof($this->members) == $this->memberCount) {
$page = 0;
} else {
$page = 1;
}
do {
$totalPages = $this->fetchPage(++$page);
} while($page < $totalPages);
$this->fetchTime = time();
}
/**
* Returns the base URL for this group's page
*
* This URL is different for groups having a custom URL.
*
* @return string The base URL for this group
*/
public function getBaseUrl() {
if(empty($this->customUrl)) {
return "http://steamcommunity.com/gid/{$this->groupId64}";
} else {
return "http://steamcommunity.com/groups/{$this->customUrl}";
}
}
/**
* Returns the custom URL of this group
*
* The custom URL is a admin specified unique string that can be used
* instead of the 64bit SteamID as an identifier for a group.
*
* @return string The custom URL of this group
*/
public function getCustomUrl() {
return $this->customUrl;
}
/**
* Returns the time this group has been fetched
*
* @return int The timestamp of the last fetch time
*/
public function getFetchTime() {
return $this->fetchTime;
}
/**
* Returns this group's 64bit SteamID
*
* @return int This group's 64bit SteamID
*/
public function getGroupId64() {
return $this->groupId64;
}
/**
* Returns the number of members this group has
*
* If the members have already been fetched the size of the member array is
* returned. Otherwise the group size is separately fetched without needing
* multiple requests for big groups.
*
* @return int The number of this group's members
* @see #fetchPage()
*/
public function getMemberCount() {
if(empty($this->memberCount)) {
$totalPages = $this->fetchPage(1);
if($totalPages == 1) {
$this->fetchTime = time();
}
}
return $this->memberCount;
}
/**
* Returns the members of this group
*
* If the members haven't been fetched yet, this is done now.
*
* @return array The Steam ID's of the members of this group
* @see #fetchMembers()
*/
public function getMembers() {
if(sizeof($this->members) != $this->memberCount) {
$this->fetchMembers();
}
return $this->members;
}
/**
* Returns whether the data for this group has already been fetched
*
* @return bool <var>true</var> if the group's members have been fetched
*/
public function isFetched() {
return !empty($this->fetchTime);
}
/**
* Fetches a specific page of the member listing of this group
*
* @param int $page The member page to fetch
* @return int The total number of pages of this group's member listing
* @see #fetchMembers()
*/
private function fetchPage($page) {
$url = "{$this->getBaseUrl()}/memberslistxml?p=$page";
$memberData = $this->getData($url);
if($page == 1) {
$this->groupId64 = (string) $memberData->groupID64;
}
$this->memberCount = (int) $memberData->memberCount;
$totalPages = (int) $memberData->totalPages;
foreach($memberData->members->steamID64 as $member) {
array_push($this->members, SteamId::create($member, false));
}
return $totalPages;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2012, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'exceptions/SteamCondenserException.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/GameStats.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/SteamGame.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/SteamGroup.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/XMLData.php';
/**
* The SteamId class represents a Steam Community profile (also called Steam
* ID)
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class SteamId extends XMLData {
/**
* @var array
*/
private static $steamIds = array();
/**
* @var string
*/
private $customUrl;
/**
* @var int
*/
private $fetchTime;
/**
* @var array
*/
private $friends;
/**
* @var array
*/
private $games;
/**
* @var bool
*/
private $limited;
/**
* @var string
*/
private $nickname;
/**
* @var array
*/
private $playtimes;
/**
* @var string
*/
private $steamId64;
/**
* @var string
*/
private $tradeBanState;
/**
* Returns whether the requested Steam ID is already cached
*
* @param string $id The custom URL of the Steam ID specified by the player
* or the 64bit SteamID
* @return bool <var>true</var> if this Steam ID is already cached
*/
public static function isCached($id) {
return array_key_exists(strtolower($id), self::$steamIds);
}
/**
* Clears the Steam ID cache
*/
public static function clearCache() {
self::$steamIds = array();
}
/**
* Converts a 64bit numeric SteamID as used by the Steam Community to a
* SteamID as reported by game servers
*
* @param string $communityId The SteamID string as used by the Steam
* Community
* @return string The converted SteamID, like <var>STEAM_0:0:12345</var>
* @throws SteamCondenserException if the community ID is to small
*/
public static function convertCommunityIdToSteamId($communityId) {
$steamId1 = substr($communityId, -1) % 2;
$steamId2a = intval(substr($communityId, 0, 4)) - 7656;
$steamId2b = substr($communityId, 4) - 1197960265728;
$steamId2b = $steamId2b - $steamId1;
if($steamId2a <= 0 && $steamId2b <= 0) {
throw new SteamCondenserException("SteamID $communityId is too small.");
}
return "STEAM_0:$steamId1:" . (($steamId2a + $steamId2b) / 2);
}
/**
* Converts a SteamID as reported by game servers to a 64bit numeric
* SteamID as used by the Steam Community
*
* @param string $steamId The SteamID string as used on servers, like
* <var>STEAM_0:0:12345</var>
* @return string The converted 64bit numeric SteamID
* @throws SteamCondenserException if the SteamID doesn't have the correct
* format
*/
public static function convertSteamIdToCommunityId($steamId) {
if($steamId == 'STEAM_ID_LAN' || $steamId == 'BOT') {
throw new SteamCondenserException("Cannot convert SteamID \"$steamId\" to a community ID.");
}
if (preg_match('/^STEAM_[0-1]:[0-1]:[0-9]+$/', $steamId)) {
$steamId = explode(':', substr($steamId, 8));
$steamId = $steamId[0] + $steamId[1] * 2 + 1197960265728;
return '7656' . $steamId;
} elseif (preg_match('/^\[U:[0-1]:[0-9]+\]$/', $steamId)) {
$steamId = explode(':', substr($steamId, 3, strlen($steamId) - 1));
$steamId = $steamId[0] + $steamId[1] + 1197960265727;
return '7656' . $steamId;
} else {
throw new SteamCondenserException("SteamID \"$steamId\" doesn't have the correct format.");
}
}
/**
* Creates a new <var>SteamID</var> instance or gets an existing one from
* the cache for the profile with the given ID
*
* @param string $id The custom URL of the Steam ID specified by player or
* the 64bit SteamID
* @param bool $fetch if <var>true</var> the profile's data is loaded into
* the object
* @param bool $bypassCache If <var>true</var> an already cached instance
* for this Steam ID will be ignored and a new one will be created
* @return SteamId The <var>SteamId</var> instance of the requested profile
*/
public static function create($id, $fetch = true, $bypassCache = false) {
$id = strtolower($id);
if(self::isCached($id) && !$bypassCache) {
$steamId = self::$steamIds[$id];
if($fetch && !$steamId->isFetched()) {
$steamId->fetchMembers();
}
return $steamId;
} else {
return new SteamId($id, $fetch);
}
}
/**
* Creates a new <var>SteamId</var> instance using a SteamID as used on
* servers
*
* The SteamID from the server is converted into a 64bit numeric SteamID
* first before this is used to retrieve the corresponding Steam Community
* profile.
*
* @param string $steamId The SteamID string as used on servers, like
* <var>STEAM_0:0:12345</var>
* @return SteamId The <var>SteamId</var> instance belonging to the given
* SteamID
* @see convertSteamIdToCommunityId()
* @see __construct()
*/
public static function getFromSteamId($steamId) {
return new SteamId(self::convertSteamIdToCommunityId($steamId));
}
/**
* Resolves a vanity URL of a Steam Community profile to a 64bit numeric
* SteamID
*
* @param string $vanityUrl The vanity URL of a Steam Community profile
* @return string The 64bit SteamID for the given vanity URL
* @throws WebApiException if the request to Steam's Web API fails
*/
public static function resolveVanityUrl($vanityUrl) {
$params = array('vanityurl' => $vanityUrl);
$json = WebApi::getJSON('ISteamUser', 'ResolveVanityURL', 1, $params);
$result = json_decode($json);
$result = $result->response;
if ($result->success != 1) {
return null;
}
return $result->steamid;
}
/**
* Creates a new <var>SteamId</var> instance for the given ID
*
* @param string $id The custom URL of the group specified by the player
* or the 64bit SteamID
* @param boolean $fetch if <var>true</var> the profile's data is loaded
* into the object
* @throws SteamCondenserException if the Steam ID data is not available,
* e.g. when it is private
*/
public function __construct($id, $fetch = true) {
if(is_numeric($id)) {
$this->steamId64 = $id;
} else {
$this->customUrl = strtolower($id);
}
if($fetch) {
$this->fetchData();
}
$this->cache();
}
/**
* Saves this <var>SteamId</var> instance in the cache
*/
public function cache() {
if(!array_key_exists($this->steamId64, self::$steamIds)) {
self::$steamIds[$this->steamId64] = $this;
if(!empty($this->customUrl) &&
!array_key_exists($this->customUrl, self::$steamIds)) {
self::$steamIds[$this->customUrl] = $this;
}
}
}
/**
* Fetchs data from the Steam Community by querying the XML version of the
* profile specified by the ID of this Steam ID
*
* @throws SteamCondenserException if the Steam ID data is not available,
* e.g. when it is private, or when it cannot be parsed
*/
public function fetchData() {
$profile = $this->getData($this->getBaseUrl() . '?xml=1');
if(!empty($profile->error)) {
throw new SteamCondenserException((string) $profile->error);
}
if(!empty($profile->privacyMessage)) {
throw new SteamCondenserException((string) $profile->privacyMessage);
}
$this->nickname = htmlspecialchars_decode((string) $profile->steamID);
$this->steamId64 = (string) $profile->steamID64;
$this->limited = (bool)(int) $profile->isLimitedAccount;
$this->tradeBanState = (string) $profile->tradeBanState;
$this->vacBanned = (bool)(int) $profile->vacBanned;
$this->imageUrl = substr((string) $profile->avatarIcon, 0, -4);
$this->onlineState = (string) $profile->onlineState;
$this->privacyState = (string) $profile->privacyState;
$this->stateMessage = (string) $profile->stateMessage;
$this->visibilityState = (int) $profile->visibilityState;
if($this->isPublic()) {
$this->customUrl = strtolower((string) $profile->customURL);
$this->headLine = htmlspecialchars_decode((string) $profile->headline);
$this->hoursPlayed = (float) $profile->hoursPlayed2Wk;
$this->location = (string) $profile->location;
$this->memberSince = (string) $profile->memberSince;
$this->realName = htmlspecialchars_decode((string) $profile->realname);
$this->steamRating = (float) $profile->steamRating;
$this->summary = htmlspecialchars_decode((string) $profile->summary);
}
if(!empty($profile->mostPlayedGames)) {
foreach($profile->mostPlayedGames->mostPlayedGame as $mostPlayedGame) {
$this->mostPlayedGames[(string) $mostPlayedGame->gameName] = (float) $mostPlayedGame->hoursPlayed;
}
}
if(!empty($profile->groups)) {
foreach($profile->groups->group as $group) {
$this->groups[] = SteamGroup::create((string) $group->groupID64, false);
}
}
if(!empty($profile->weblinks)) {
foreach($profile->weblinks->weblink as $link) {
$this->links[htmlspecialchars_decode((string) $link->title)] = (string) $link->link;
}
}
$this->fetchTime = time();
}
/**
* Fetches the friends of this user
*
* This creates a new <var>SteamId</var> instance for each of the friends
* without fetching their data.
*
* @see getFriends()
* @see __construct()
* @throws SteamCondenserException if an error occurs while parsing the
* data
*/
private function fetchFriends() {
$friendsData = $this->getData($this->getBaseUrl() . '/friends?xml=1');
$this->friends = array();
foreach($friendsData->friends->friend as $friend) {
$this->friends[] = SteamId::create((string) $friend, false);
}
}
/**
* Fetches the games this user owns
*
* @see getGames()
* @throws SteamCondenserException if an error occurs while parsing the
* data
*/
private function fetchGames() {
$gamesData = $this->getData($this->getBaseUrl() . '/games?xml=1');
$this->games = array();
$this->playtimes = array();
foreach($gamesData->games->game as $gameData) {
$appId = (int) $gameData->appID;
$game = SteamGame::create($appId, $gameData);
$this->games[$appId] = $game;
$recent = (float) $gameData->hoursLast2Weeks;
$total = (float) str_replace(',', '', $gameData->hoursOnRecord);
$playtimes = array((int) ($recent * 60), (int) ($total * 60));
$this->playtimes[$appId] = $playtimes;
}
}
/**
* Tries to find a game instance with the given application ID or full name
* or short name
*
* @param mixed $id The full or short name or the application ID of the
* game
* @return SteamGame The game found with the given ID
* @throws SteamCondenserException if the user does not own the game or no
* game with the given ID exists
*/
private function findGame($id) {
$game = null;
foreach($this->getGames() as $currentGame) {
if($currentGame->getAppId() == $id ||
$currentGame->getShortName() == $id ||
$currentGame->getName() == $id) {
$game = $currentGame;
break;
}
}
if($game == null) {
if(is_int($id)) {
$message = "This SteamID does not own a game with application ID {$id}.";
} else {
$message = "This SteamID does not own the game \"{$id}\".";
}
throw new SteamCondenserException($message);
}
return $game;
}
/**
* Returns the base URL for this Steam ID
*
* This URL is different for Steam IDs having a custom URL.
*
* @return string The base URL for this SteamID
*/
public function getBaseUrl() {
if(empty($this->customUrl)) {
return "http://steamcommunity.com/profiles/{$this->steamId64}";
} else {
return "http://steamcommunity.com/id/{$this->customUrl}";
}
}
/**
* Returns the custom URL of this Steam ID
*
* The custom URL is a user specified unique string that can be used
* instead of the 64bit SteamID as an identifier for a Steam ID.
*
* <strong>Note:</strong> The custom URL is not necessarily the same as the
* user's nickname.
*
* @return string The custom URL of this Steam ID
*/
public function getCustomUrl() {
return $this->customUrl;
}
/**
* Returns the time this group has been fetched
*
* @return int The timestamp of the last fetch time
*/
public function getFetchTime() {
return $this->fetchTime;
}
/**
* Returns the Steam Community friends of this user
*
* If the friends haven't been fetched yet, this is done now.
*
* @return array The friends of this user
* @see fetchFriends()
*/
public function getFriends() {
if(empty($this->friends)) {
$this->fetchFriends();
}
return $this->friends;
}
/**
* Returns the URL of the full-sized version of this user's avatar
*
* @return string The URL of the full-sized avatar
*/
public function getFullAvatarUrl() {
return $this->imageUrl . '_full.jpg';
}
/**
* Returns the games this user owns
*
* The keys of the hash are the games' application IDs and the values are
* the corresponding game instances.
*
* If the friends haven't been fetched yet, this is done now.
*
* @return array The games this user owns
* @see fetchGames()
*/
public function getGames() {
if(empty($this->games)) {
$this->fetchGames();
}
return $this->games;
}
/**
* Returns the stats for the given game for the owner of this SteamID
*
* @param mixed $id The full or short name or the application ID of the
* game stats should be fetched for
* @return GameStats The statistics for the game with the given name
* @throws SteamCondenserException if the user does not own this game or it
* does not have any stats
*/
public function getGameStats($id) {
$game = $this->findGame($id);
if(!$game->hasStats()) {
throw new SteamCondenserException("\"{$game->getName()}\" does not have stats.");
}
if(empty($this->customUrl)) {
return GameStats::createGameStats($this->steamId64, $game->getShortName());
} else {
return GameStats::createGameStats($this->customUrl, $game->getShortName());
}
}
/**
* Returns the URL of the icon version of this user's avatar
*
* @return string The URL of the icon-sized avatar
*/
public function getIconAvatarUrl() {
return $this->imageUrl . '.jpg';
}
/**
* Returns a unique identifier for this Steam ID
*
* This is either the 64bit numeric SteamID or custom URL
*
* @return string The 64bit numeric SteamID or the custom URL
*/
public function getId() {
if($this->customUrl == null) {
return $this->steamId64;
} else {
return $this->customUrl;
}
}
/**
* Returns the URL of the medium-sized version of this user's avatar
*
* @return string The URL of the medium-sized avatar
*/
public function getMediumAvatarUrl() {
return $this->imageUrl . '_medium.jpg';
}
/**
* Returns the Steam nickname of the user
*
* @return string The Steam nickname of the user
*/
public function getNickname() {
return $this->nickname;
}
/**
* @return String
*/
public function getSteamId64() {
return $this->steamId64;
}
/**
* Returns the time in minutes this user has played this game in the last
* two weeks
*
* @param mixed $id The full or short name or the application ID of the
* game
* @return int The number of minutes this user played the given game in the
* last two weeks
*/
public function getRecentPlaytime($id) {
$game = $this->findGame($id);
$playtimes = $this->playtimes[$game->getAppId()];
return $playtimes[0];
}
/**
* Returns the total time in minutes this user has played this game
*
* @param mixed $id The full or short name or the application ID of the
* game
* @return int The total number of minutes this user played the given game
*/
public function getTotalPlaytime($id) {
$game = $this->findGame($id);
$playtimes = $this->playtimes[$game->getAppId()];
return $playtimes[1];
}
/**
* Returns this user's ban state in Steam's trading system
*
* @return string This user's trading ban state
*/
public function getTradeBanState() {
return $this->tradeBanState;
}
/**
* Returns whether the owner of this Steam ID is VAC banned
*
* @return bool <var>true</var> if the user has been banned by VAC
*/
public function isBanned() {
return $this->vacBanned;
}
/**
* Returns whether the data for this Steam ID has already been fetched
*
* @return bool <var>true</var> if the Steam ID's data has been
* fetched
*/
public function isFetched() {
return !empty($this->fetchTime);
}
/**
* Returns whether the owner of this Steam ID is playing a game
*
* @return bool <var>true</var> if the user is in-game
*/
public function isInGame() {
return $this->onlineState == 'in-game';
}
/**
* Returns whether this Steam account is limited
*
* @return bool <var>true</var> if this account is limited
*/
public function isLimited() {
return $this->limited;
}
/**
* Returns whether the owner of this Steam ID is currently logged into
* Steam
*
* @return bool <var>true</var> if the user is online
*/
public function isOnline() {
return ($this->onlineState == 'online') || ($this->onlineState == 'in-game');
}
/**
* Returns whether this Steam ID is publicly accessible
*
* @return bool <var>true</var> if this Steam ID is public
*/
public function isPublic() {
return $this->privacyState == 'public';
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2011-2012, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/tf2/TF2Inventory.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/tf2/TF2Item.php';
/**
* Represents the inventory (aka. Backpack) of a player of the public Team
* Fortress 2 beta
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class TF2BetaInventory extends TF2Inventory {
const APP_ID = 520;
/**
* This checks the cache for an existing inventory. If it exists it is
* returned. Otherwise a new inventory is created.
*
* @param string $steamId The 64bit Steam ID or vanity URL of the user
* @param bool $fetchNow Whether the data should be fetched now
* @param bool $bypassCache Whether the cache should be bypassed
* @return TF2BetaInventory The inventory created from the given options
*/
public static function createInventory($steamId, $fetchNow = true, $bypassCache = false) {
return parent::create(self::APP_ID, $steamId, $fetchNow, $bypassCache);
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/GameClass.php';
/**
* Represents the stats for a Team Fortress 2 class for a specific user
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage communty
*/
class TF2Class extends GameClass {
/**
* @var int
*/
private $maxBuildingsDestroyed;
/**
* @var int
*/
private $maxCaptures;
/**
* @var int
*/
private $maxDamage;
/**
* @var int
*/
private $maxDefenses;
/**
* @var int
*/
private $maxDominations;
/**
* @var int
*/
private $maxKillAssists;
/**
* @var int
*/
private $maxKills;
/**
* @var int
*/
private $maxRevenges;
/**
* @var int
*/
private $maxScore;
/**
* @var int
*/
private $maxTimeAlive;
/**
* @var int
*/
private $playTime;
/**
* Creates a new TF2 class instance based on the assigned XML data
*
* @param SimpleXMLElement $classData The XML data for this class
*/
public function __construct(SimpleXMLElement $classData) {
$this->name = (string) $classData->className;
$this->maxBuildingsDestroyed = (int) $classData->ibuildingsdestroyed;
$this->maxCaptures = (int) $classData->ipointcaptures;
$this->maxDamage = (int) $classData->idamagedealt;
$this->maxDefenses = (int) $classData->ipointdefenses;
$this->maxDominations = (int) $classData->idominations;
$this->maxKillAssists = (int) $classData->ikillassists;
$this->maxKills = (int) $classData->inumberofkills;
$this->maxRevenges = (int) $classData->irevenge;
$this->maxScore = (int) $classData->ipointsscored;
$this->maxTimeAlive = (int) $classData->iplaytime;
$this->playTime = (int) $classData->playtimeSeconds;
}
/**
* Returns the maximum number of buildings the player has destroyed in a
* single life with this class
*
* @return int Maximum number of buildings destroyed
*/
public function getMaxBuildingsDestroyed() {
return $this->maxBuildingsDestroyed;
}
/**
* Returns the maximum number of points captured by the player in a single
* life with this class
*
* @return int Maximum number of points captured
*/
public function getMaxCaptures() {
return $this->maxCaptures;
}
/**
* Returns the maximum damage dealt by the player in a single life with
* this class
*
* @return int Maximum damage dealt
*/
public function getMaxDamage() {
return $this->maxDamage;
}
/**
* Returns the maximum number of defenses by the player in a single life
* with this class
*
* @return int Maximum number of defenses
*/
public function getMaxDefenses() {
return $this->maxDefnses;
}
/**
* Returns the maximum number of dominations by the player in a single life
* with this class
*
* @return int Maximum number of dominations
*/
public function getMaxDominations() {
return $this->maxDominations;
}
/**
* Returns the maximum number of times the the player assisted a teammate
* with killing an enemy in a single life with this class
*
* @return int Maximum number of kill assists
*/
public function getMaxKillAssists() {
return $this->maxKillAssists;
}
/**
* Returns the maximum number of enemies killed by the player in a single
* life with this class
*
* @return int Maximum number of kills
*/
public function getMaxKills() {
return $this->maxKills;
}
/**
* Returns the maximum number of revenges by the player in a single life
* with this class
*
* @return int Maximum number of revenges
*/
public function getMaxRevenges() {
return $this->maxRevenges;
}
/**
* Returns the maximum number score achieved by the player in a single life
* with this class
*
* @return int Maximum score
*/
public function getMaxScore() {
return $this->maxScore;
}
/**
* Returns the maximum lifetime by the player in a single life with this
* class
*
* @return int Maximum lifetime
*/
public function getMaxTimeAlive() {
return $this->maxTimeAlive;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/tf2/TF2Class.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/tf2/TF2Engineer.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/tf2/TF2Medic.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/tf2/TF2Sniper.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/tf2/TF2Spy.php';
/**
* The <var>TF2ClassFactory</var> is used to created instances of
* <var>TF2Class</var> based on the XML input data
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
abstract class TF2ClassFactory {
/**
* Creates a new instance of a TF2 class instance based on the given XML
* data
*
* This returns an instance of <var>TF2Class</var> or its subclasses
* <var>TF2Engineer</var>, <var>TF2Medic</var>, <var>TF2Sniper</var> or
* <var>TF2Spy</var> depending on the given XML data.
*
* @param SimpleXMLElement $classData The XML data for the class
* @return TF2Class The statistics for the given class data
*/
public static function getTF2Class(SimpleXMLElement $classData) {
switch($classData->className) {
case 'Engineer':
return new TF2Engineer($classData);
case 'Medic':
return new TF2Medic($classData);
case 'Sniper':
return new TF2Sniper($classData);
case 'Spy':
return new TF2Spy($classData);
default:
return new TF2Class($classData);
}
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/tf2/TF2Class.php';
/**
* Represents the stats for the Team Fortress 2 Engineer class for a specific
* user
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class TF2Engineer extends TF2Class {
/**
* @var int
*/
private $maxBuildingsBuilt;
/**
* @var int
*/
private $maxSentryKills;
/**
* @var int
*/
private $maxTeleports;
/**
* Creates a new instance of the Engineer class based on the given XML data
*
* @param SimpleXMLElement $classData The XML data for this Engineer
*/
public function __construct(SimpleXMLElement $classData) {
parent::__construct($classData);
$this->maxBuildingsBuilt = (int) $classData->ibuildingsbuilt;
$this->maxTeleports = (int) $classData->inumteleports;
$this->maxSentryKills = (int) $classData->isentrykills;
}
/**
* Returns the maximum number of buildings built by the player in a single
* life as an Engineer
*
* @return int Maximum number of buildings built
*/
public function getMaxBuildingsBuilt() {
return $this->maxBuildingsBuilt;
}
/**
* Returns the maximum number of enemies killed by sentry guns built by the
* player in a single life as an Engineer
*
* @return int Maximum number of sentry kills
*/
public function getMaxSentryKills() {
return $this->maxSentryKills;
}
/**
* Returns the maximum number of teammates teleported by teleporters built
* by the player in a single life as an Engineer
*
* @return int Maximum number of teleports
*/
public function getMaxTeleports() {
return $this->maxTeleports;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2010-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/SteamId.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/WebApi.php';
/**
* Represents the special Team Fortress 2 item Golden Wrench. It includes the
* ID of the item, the serial number of the wrench, a reference to the SteamID
* of the owner and the date this player crafted the wrench
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class TF2GoldenWrench {
/**
* @var array
*/
private static $goldenWrenches = null;
/**
* @var int
*/
private $date;
/**
* @var int
*/
private $id;
/**
* @var int
*/
private $number;
/**
* @var SteamId
*/
private $owner;
/**
* Returns all Golden Wrenches
*
* @return All Golden Wrenches
* @throws SteamCondenserException If an error occurs querying the Web API
* or the Steam Community
*/
public static function getGoldenWrenches() {
if(self::$goldenWrenches == null) {
self::$goldenWrenches = array();
$data = json_decode(WebApi::getJSON('ITFItems_440', 'GetGoldenWrenches', 2));
foreach($data->results->wrenches as $wrenchData) {
self::$goldenWrenches[] = new TF2GoldenWrench($wrenchData);
}
}
return self::$goldenWrenches;
}
/**
* Creates a new instance of a Golden Wrench with the given data
*
* @param stdClass $wrenchData The JSON data for this wrench
* @throws SteamCondenserException If the SteamId for the owner of the
* wrench cannot be created
*/
private function __construct($wrenchData) {
$this->date = (int) $wrenchData->timestamp;
$this->id = (int) $wrenchData->itemID;
$this->number = (int) $wrenchData->wrenchNumber;
$this->owner = SteamId::create((string) $wrenchData->steamID, false);
}
/**
* Returns the date this Golden Wrench has been crafted
*
* @return int The crafting date of this wrench
*/
public function getDate() {
return $this->date;
}
/**
* Returns the unique item ID of this Golden Wrench
*
* @return int The ID of this wrench
*/
public function getId() {
return $this->id;
}
/**
* Returns the serial number of this Golden Wrench
*
* @return The serial of this wrench
*/
public function getNumber() {
return $this->number;
}
/**
* Returns the SteamID of the owner of this Golden Wrench
*
* @return SteamId The owner of this wrench
*/
public function getOwner() {
return $this->owner;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2010-2012, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/tf2/TF2Item.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/GameInventory.php';
/**
* Represents the inventory (aka. Backpack) of a Team Fortress 2 player
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class TF2Inventory extends GameInventory {
const APP_ID = 440;
const ITEM_CLASS = 'TF2Item';
/**
* This checks the cache for an existing inventory. If it exists it is
* returned. Otherwise a new inventory is created.
*
* @param string $steamId The 64bit Steam ID or vanity URL of the user
* @param bool $fetchNow Whether the data should be fetched now
* @param bool $bypassCache Whether the cache should be bypassed
* @return TF2Inventory The inventory created from the given options
*/
public static function createInventory($steamId, $fetchNow = true, $bypassCache = false) {
return parent::create(self::APP_ID, $steamId, $fetchNow, $bypassCache);
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2010-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/GameItem.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/tf2/TF2Inventory.php';
/**
* Represents a Team Fortress 2 item
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class TF2Item extends GameItem {
/**
* @var array The names of the Team Fortress 2 classes
*/
private static $CLASSES = array('scout', 'sniper', 'soldier', 'demoman',
'medic', 'heavy', 'pyro', 'spy');
/**
* @var array
*/
private $equipped;
/**
* Creates a new instance of a TF2Item with the given data
*
* @param $inventory The inventory this item is contained in
* @param $itemData The data specifying this item
* @throws WebApiException on Web API errors
*/
public function __construct(TF2Inventory $inventory, $itemData) {
parent::__construct($inventory, $itemData);
$this->equipped = array();
foreach(self::$CLASSES as $classId => $className) {
$this->equipped[$className] = ($itemData->inventory & (1 << 16 + $classId) != 0);
}
}
/**
* Returns the class symbols for each class this player has equipped this
* item
*
* @return array The names of the classes this player has equipped this
* item
*/
public function getClassesEquipped() {
$classesEquipped = array();
foreach($this->equipped as $classId => $equipped) {
if($equipped) {
$classesEquipped[] = $classId;
}
}
return $classesEquipped;
}
/**
* Returns whether this item is equipped by this player at all
*
* @return bool <var>true</var> if the player has equipped this item at all
*/
public function isEquipped() {
return in_array(true, $this->equipped);
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/tf2/TF2Class.php';
/**
* Represents the stats for the Team Fortress 2 Medic class for a specific user
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class TF2Medic extends TF2Class {
/**
* @var int
*/
private $maxHealthHealed;
/**
* @var int
*/
private $maxUberCharges;
/**
* Creates a new instance of the Medic class based on the given XML data
*
* @param SimpleXMLElement $classData The XML data for this Medic
*/
public function __construct(SimpleXMLElement $classData) {
parent::__construct($classData);
$this->maxUberCharges = (int) $classData->inuminvulnerable;
$this->maxHealthHealed = (int) $classData->ihealthpointshealed;
}
/**
* Returns the maximum health healed for teammates by the player in a
* single life as a Medic
*
* @return Maximum health healed
*/
public function getMaxHealthHealed() {
return $this->maxHealthHealed;
}
/**
* Returns the maximum number of ÜberCharges provided by the player in a
* single life as a Medic
*
* @return Maximum number of ÜberCharges
*/
public function getMaxUbercharges() {
return $this->maxUbercharges;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/tf2/TF2Class.php';
/**
* Represents the stats for the Team Fortress 2 Sniper class for a specific
* user
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class TF2Sniper extends TF2Class {
/**
* @var int
*/
private $maxHeadshots;
/**
* Creates a new instance of the Sniper class based on the given XML data
*
* @param SimpleXMLElement $classData The XML data for this Sniper
*/
public function __construct(SimpleXMLElement $classData) {
parent::__construct($classData);
$this->maxHeadshots = (int) $classData->iheadshots;
}
/**
* Returns the maximum number enemies killed with a headshot by the player
* in single life as a Sniper
*
* @return int Maximum number of headshots
*/
public function getMaxHeadshots() {
return $this->maxHeadshots;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2012, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/tf2/TF2Class.php';
/**
* Represents the stats for the Team Fortress 2 Spy class for a specific user
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class TF2Spy extends TF2Class {
/**
* @var int
*/
private $maxBackstabs;
/**
* @var int
*/
private $maxHeadShots;
/**
* @var int
*/
private $maxHealthLeeched;
/**
* Creates a new instance of the Spy class based on the given XML data
*
* @param SimpleXMLElement $classData The XML data for this Spy
*/
public function __construct(SimpleXMLElement $classData) {
parent::__construct($classData);
$this->maxBackstabs = (int) $classData->ibackstabs;
$this->maxHeadShots = (int) $classData->iheadshots;
$this->maxHealthLeeched = (int) $classData->ihealthpointsleached;
}
/**
* Returns the maximum health leeched from enemies by the player in a single
* life as a Spy
*
* @return int Maximum health leeched
*/
public function getMaxBackstabs() {
return $this->maxBackstabs;
}
/**
* Returns the head shots by the player in a single life as a Spy
*
* @return int Maximum number of head shots
*/
public function getMaxHeadShots()
{
return $this->maxHeadShots;
}
/**
* Returns the maximum health leeched from enemies by the player in a single
* life as a Spy
*
* @return int Maximum health leeched
*/
public function getMaxHealthLeeched() {
return $this->maxHealthLeeched;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2013, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/community/GameStats.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/tf2/TF2BetaInventory.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/tf2/TF2ClassFactory.php';
require_once STEAM_CONDENSER_PATH . 'steam/community/tf2/TF2Inventory.php';
/**
* This class represents the game statistics for a single user in Team Fortress
* 2
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class TF2Stats extends GameStats {
/**
* @var int
*/
private $accumulatedPoints;
/**
* @var array
*/
private $classStats;
/**
* @var TF2Inventory
*/
private $inventory;
/**
* @var int
*/
private $totalPlaytime;
/**
* Creates a new <var>TF2Stats</var> instance by calling the super
* constructor with the game name <var>"tf2"</var>
*
* @param string $steamId The custom URL or 64bit Steam ID of the user
* @param bool $beta if <var>true</var>, creates stats for the public TF2
* beta
*/
public function __construct($steamId, $beta = false) {
parent::__construct($steamId, ($beta ? '520' : 'tf2'));
if($this->isPublic()) {
if(!empty($this->xmlData->stats->accumulatedPoints)) {
$this->accumulatedPoints = (int) $this->xmlData->stats->accumulatedPoints;
}
if(!empty($this->xmlData->stats->secondsPlayedAllClassesLifetime)) {
$this->totalPlaytime = (int) $this->xmlData->stats->secondsPlayedAllClassesLifetime;
}
}
}
/**
* Returns the total points this player has achieved in his career
*
* @return int This player's accumulated points
*/
public function getAccumulatedPoints() {
return $this->accumulatedPoints;
}
/**
* Returns the statistics for all Team Fortress 2 classes for this user
*
* If the classes haven't been parsed already, parsing is done now.
*
* @return array An array storing individual stats for each Team Fortress 2
* class
*/
public function getClassStats() {
if(!$this->isPublic()) {
return null;
}
if(empty($this->classStats)) {
foreach($this->xmlData->stats->classData as $classData) {
$this->classStats[$classData->className] = TF2ClassFactory::getTF2Class($classData);
}
}
return $this->classStats;
}
/**
* Returns the current Team Fortress 2 inventory (a.k.a. backpack) of this
* player
*
* @return TF2Inventory This player's TF2 backpack
*/
public function getInventory() {
if(!$this->isPublic()) {
return null;
}
if(empty($this->inventory)) {
$this->inventory = TF2Inventory::createInventory($this->user->getId());
}
return $this->inventory;
}
/**
* Returns the accumulated number of seconds this player has spent playing
* as a TF2 class
*
* @return int Total seconds played as a TF2 class
*/
public function getTotalPlaytime() {
return $this->totalPlaytime;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2010-2013, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'exceptions/WebApiException.php';
/**
* This adds support for Steam Web API to classes needing this functionality.
* The Web API requires you to register a domain with your Steam account to
* acquire an API key. See http://steamcommunity.com/dev for further details.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
class WebApi {
/**
* @var Monolog\Logger The Monolog logger for this class
*/
private static $log;
/**
* @var string
*/
private static $apiKey = null;
/**
* @var WebApi
*/
protected static $instance = null;
/**
* @var bool
*/
protected static $secure = true;
/**
* Returns the Steam Web API key
*
* @return string The Steam Web API key
*/
public static function getApiKey() {
return self::$apiKey;
}
/**
* Returns a raw list of interfaces and their methods that are available in
* Steam's Web API
*
* This can be used for reference when accessing interfaces and methods
* that have not yet been implemented by Steam Condenser.
*
* @return array The list of interfaces and methods
*/
public static function getInterfaces() {
$data = self::getJSON('ISteamWebAPIUtil', 'GetSupportedAPIList');
return json_decode($data)->apilist->interfaces;
}
/**
* Fetches JSON data from Steam Web API using the specified interface,
* method and version. Additional parameters are supplied via HTTP GET.
*
* @param string $interface The Web API interface to call, e.g. ISteamUser
* @param string $method The Web API method to call, e.g.
* GetPlayerSummaries
* @param int $version The API method version to use
* @param array $params Additional parameters to supply via HTTP GET
* @return string Data is returned as a JSON-encoded string.
*/
public static function getJSON($interface, $method, $version = 1, $params = null) {
return self::instance()->_getJSON($interface, $method, $version, $params);
}
/**
* Fetches JSON data from Steam Web API using the specified interface,
* method and version. Additional parameters are supplied via HTTP GET.
*
* @param string $interface The Web API interface to call, e.g. ISteamUser
* @param string $method The Web API method to call, e.g.
* GetPlayerSummaries
* @param int $version The API method version to use
* @param array $params Additional parameters to supply via HTTP GET
* @return stdClass Data is returned as a json_decoded object
*/
public static function getJSONData($interface, $method, $version = 1, $params = null) {
return self::instance()->_getJSONData($interface, $method, $version, $params);
}
/**
* Fetches data from Steam Web API using the specified interface, method
* and version. Additional parameters are supplied via HTTP GET. Data is
* returned as a String in the given format.
*
* @param string $format The format to load from the API ('json', 'vdf', or
* 'xml')
* @param string $interface The Web API interface to call, e.g. ISteamUser
* @param string $method The Web API method to call, e.g.
* GetPlayerSummaries
* @param int $version The API method version to use
* @param array $params Additional parameters to supply via HTTP GET
* @return string Data is returned as a String in the given format (which
* may be 'json', 'vdf' or 'xml').
*/
public static function load($format, $interface, $method, $version = 1, $params = null) {
return self::instance()->_load($format, $interface, $method, $version, $params);
}
/**
* Sets whether HTTPS should be used for the communication with the Web API
*
* @param bool $secure Whether to use HTTPS
*/
public static function setSecure($secure) {
self::$secure = $secure;
}
/**
* Returns a singleton instance of an internal <var>WebApi</var> object
*
* @return WebApi The internal <var>WebApi</var> instance
*/
private static function instance() {
if (self::$instance == null) {
self::$instance = new WebApi();
self::$log = new \Monolog\Logger('WebApi');
}
return self::$instance;
}
/**
* Sets the Steam Web API key
*
* @param string $apiKey The 128bit API key that has to be requested from
* http://steamcommunity.com/dev
* @throws WebApiException if the given API key is not a valid 128bit
* hexadecimal string
*/
public static function setApiKey($apiKey) {
if($apiKey != null && !preg_match('/^[0-9A-F]{32}$/', $apiKey)) {
throw new WebApiException(WebApiException::INVALID_KEY);
}
self::$apiKey = $apiKey;
}
/**
* Private constructor to prevent direct usage of <var>WebApi</var>
* instances
*/
private function __construct() {}
/**
* Fetches JSON data from Steam Web API using the specified interface,
* method and version. Additional parameters are supplied via HTTP GET.
*
* @param string $interface The Web API interface to call, e.g. ISteamUser
* @param string $method The Web API method to call, e.g.
* GetPlayerSummaries
* @param int $version The API method version to use
* @param array $params Additional parameters to supply via HTTP GET
* @return string Data is returned as a JSON-encoded string.
*/
protected function _getJSON($interface, $method, $version = 1, $params = null) {
return $this->load('json', $interface, $method, $version, $params);
}
/**
* Fetches JSON data from Steam Web API using the specified interface,
* method and version. Additional parameters are supplied via HTTP GET.
*
* @param string $interface The Web API interface to call, e.g. ISteamUser
* @param string $method The Web API method to call, e.g.
* GetPlayerSummaries
* @param int $version The API method version to use
* @param array $params Additional parameters to supply via HTTP GET
* @return stdClass Data is returned as a json_decoded object
* @throws WebApiException In case of any request failure
*/
protected function _getJSONData($interface, $method, $version = 1, $params = null) {
$data = $this->getJSON($interface, $method, $version, $params);
$result = json_decode($data)->result;
if($result->status != 1) {
throw new WebApiException(WebApiException::STATUS_BAD, $result->status, $result->statusDetail);
}
return $result;
}
/**
* Fetches data from Steam Web API using the specified interface, method
* and version. Additional parameters are supplied via HTTP GET. Data is
* returned as a String in the given format.
*
* @param string $format The format to load from the API ('json', 'vdf', or
* 'xml')
* @param string $interface The Web API interface to call, e.g. ISteamUser
* @param string $method The Web API method to call, e.g.
* GetPlayerSummaries
* @param int $version The API method version to use
* @param array $params Additional parameters to supply via HTTP GET
* @return string Data is returned as a String in the given format (which
* may be 'json', 'vdf' or 'xml').
*/
protected function _load($format, $interface, $method, $version = 1, $params = null) {
$version = str_pad($version, 4, '0', STR_PAD_LEFT);
$protocol = (self::$secure) ? 'https' : 'http';
$url = "$protocol://api.steampowered.com/$interface/$method/v$version/";
$params['format'] = $format;
if (self::$apiKey != null) {
$params['key'] = self::$apiKey;
}
if($params != null && !empty($params)) {
$url .= '?';
$url_params = array();
foreach($params as $k => $v) {
$url_params[] = "$k=$v";
}
$url .= join('&', $url_params);
}
return $this->request($url);
}
/**
* Fetches data from Steam Web API using the specified URL
*
* @param string $url The URL to load
* @return string The data returned by the Web API
* @throws WebApiException if the request failed
*/
protected function request($url) {
self::$log->addDebug("Querying Steam Web API: " . str_replace(self::$apiKey, 'SECRET', $url));
$data = @file_get_contents($url);
if(empty($data)) {
preg_match('/^.* (\d{3}) (.*)$/', $http_response_header[0], $http_status);
if($http_status[1] == 401) {
throw new WebApiException(WebApiException::UNAUTHORIZED);
}
throw new WebApiException(WebApiException::HTTP_ERROR, $http_status[1], $http_status[2]);
}
return $data;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2009-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'exceptions/SteamCondenserException.php';
/**
* This class is used to streamline access to XML-based data resources
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage community
*/
abstract class XMLData {
/**
* Loads XML data from the given URL and returns it parsed into a
* <var>SimpleXMLElement</var>
*
* @param string $url The URL to load the data from
* @return SimpleXMLElement The parsed XML data
* @throws SteamCondenserException if the data cannot be parsed
*/
protected function getData($url) {
try {
return @new SimpleXMLElement($url, 0, true);
} catch (Exception $e) {
throw new SteamCondenserException("XML could not be parsed", 0, $e);
}
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/packets/SteamPacket.php';
/**
* This packet class represents a A2M_GET_SERVERS_BATCH2 request sent to a
* master server
*
* It is used to receive a list of game servers matching the specified filters.
*
* Filtering:
* Instead of filtering the results sent by the master server locally, you
* should at least use the following filters to narrow down the results sent by
* the master server. Receiving all servers from the master server is taking
* quite some time.
*
* Available filters:
* <ul>
* <li><var>\type\d</var>: Request only dedicated servers</li>
* <li><var>\secure\1</var>: Request only secure servers</li>
* <li><var>\gamedir\[mod]</var>: Request only servers of a specific mod</li>
* <li><var>\map\[mapname]</var>: Request only servers running a specific
* map</li>
* <li><var>\linux\1</var>: Request only linux servers</li>
* <li><var>\emtpy\1</var>: Request only <b>non</b>-empty servers</li>
* <li><var>\full\1</var>: Request only servers <b>not</b> full</li>
* <li><var>\proxy\1</var>: Request only spectator proxy servers</li>
* </ul>
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage packets
* @see MasterServer::getServers()
*/
class A2M_GET_SERVERS_BATCH2_Packet extends SteamPacket {
private $filter;
private $regionCode;
private $startIp;
/**
* Creates a new A2M_GET_SERVERS_BATCH2 request object, filtering by the
* given paramters
*
* @param int $regionCode The region code to filter servers by region.
* @param string $startIp This should be the last IP received from the
* master server or 0.0.0.0
* @param string $filter The filters to apply in the form
* ("\filtername\value...")
*/
public function __construct($regionCode = MasterServer::REGION_ALL, $startIp = '0.0.0.0', $filter = '') {
parent::__construct(SteamPacket::A2M_GET_SERVERS_BATCH2_HEADER);
$this->filter = $filter;
$this->regionCode = $regionCode;
$this->startIp = $startIp;
}
/**
* Returns the raw data representing this packet
*
* @return string A string containing the raw data of this request packet
*/
public function __toString() {
return chr($this->headerData) . chr($this->regionCode) . $this->startIp . "\0" . $this->filter . "\0";
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/packets/SteamPacket.php';
/**
* This packet class class represents a A2S_INFO request send to a game server
*
* It will cause the server to send some basic information about itself, e.g.
* the running game, map and the number of players.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage packets
* @see GameServer::updateServerInfo()
*/
class A2S_INFO_Packet extends SteamPacket {
/**
* Creates a new A2S_INFO request object
*/
public function __construct() {
parent::__construct(SteamPacket::A2S_INFO_HEADER, "Source Engine Query\0");
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/packets/RequestPacketWithChallenge.php';
/**
* This packet class represents a A2S_PLAYER request send to a game server
*
* It is used to request the list of players currently playing on the server.
*
* This packet type requires the client to challenge the server in advance,
* which is done automatically if required.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage packets
* @see GameServer::updatePlayerInfo()
*/
class A2S_PLAYER_Packet extends RequestPacketWithChallenge {
/**
* Creates a new A2S_PLAYER request object including the challenge number
*
* @param int $challengeNumber The challenge number received from the
* server
*/
public function __construct($challengeNumber = 0xFFFFFFFF) {
parent::__construct(SteamPacket::A2S_PLAYER_HEADER, $challengeNumber);
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/packets/RequestPacketWithChallenge.php';
/**
* This packet class represents a A2S_RULES request send to a game server
*
* The game server will return a list of currently active game rules, e.g.
* <var>mp_friendlyfire = 1</var>.
*
* This packet type requires the client to challenge the server in advance,
* which is done automatically if required.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage packets
* @see GameServer::updateRulesInfo()
*/
class A2S_RULES_Packet extends RequestPacketWithChallenge {
/**
* Creates a new A2S_RULES request object including the challenge number
*
* @param int $challengeNumber The challenge number received from the
* server
*/
public function __construct($challengeNumber = 0xFFFFFFFF)
{
parent::__construct(SteamPacket::A2S_RULES_HEADER, $challengeNumber);
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/packets/SteamPacket.php';
/**
* This packet class represents a A2S_SERVERQUERY_GETCHALLENGE request send to
* a game server
*
* It is used to retrieve a challenge number from the game server, which helps
* to identify the requesting client.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage packets
* @see GameServer::updateChallengeNumber()
*/
class A2S_SERVERQUERY_GETCHALLENGE_Packet extends SteamPacket {
/**
* Creates a new A2S_SERVERQUERY_GETCHALLENGE request object
*/
public function __construct() {
parent::__construct(SteamPacket::A2S_SERVERQUERY_GETCHALLENGE_HEADER);
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/packets/SteamPacket.php';
/**
* This packet class represents a M2A_SERVER_BATCH response replied by a master
* server
*
* It contains a list of IP addresses and ports of game servers matching the
* requested criteria.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage packets
* @see MasterServer::getServers()
*/
class M2A_SERVER_BATCH_Packet extends SteamPacket {
/**
* @var array
*/
private $serverArray;
/**
* Creates a new M2A_SERVER_BATCH response object based on the given data
*
* @param string $data The raw packet data replied from the server
* @throws PacketFormatException if the packet data is not well formatted
*/
public function __construct($data) {
parent::__construct(SteamPacket::M2A_SERVER_BATCH_HEADER, $data);
if($this->contentData->getByte() != 10) {
throw new PacketFormatException('Master query response is missing additional 0x0A byte.');
}
do {
$firstOctet = $this->contentData->getByte();
$secondOctet = $this->contentData->getByte();
$thirdOctet = $this->contentData->getByte();
$fourthOctet = $this->contentData->getByte();
$portNumber = $this->contentData->getShort();
$portNumber = (($portNumber & 0xFF) << 8) + ($portNumber >> 8);
$this->serverArray[] = "$firstOctet.$secondOctet.$thirdOctet.$fourthOctet:$portNumber";
} while($this->contentData->remaining() > 0);
}
/**
* Returns the list of servers returned from the server in this packet
*
* @return array An array of server addresses (i.e. IP addresses + port
* numbers)
*/
public function getServers() {
return $this->serverArray;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/packets/rcon/RCONPacket.php';
/**
* This packet class represents a SERVERDATA_AUTH request sent to a Source
* server
*
* It is used to authenticate the client for RCON communication.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage rcon-packets
* @see SourceServer::rconAuth()
*/
class RCONAuthRequest extends RCONPacket {
/**
* Creates a RCON authentication request for the given request ID and RCON
* password
*
* @param int $requestId The request ID of the RCON connection
* @param string $password The RCON password of the server
*/
public function __construct($requestId, $password) {
parent::__construct($requestId, RCONPacket::SERVERDATA_AUTH, $password);
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/packets/rcon/RCONPacket.php';
/**
* This packet class represents a SERVERDATA_AUTH_RESPONSE packet sent by a
* Source server
*
* It is used to indicate the success or failure of an authentication attempt
* of a client for RCON communication.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage rcon-packets
* @see SourceServer::rconAuth()
*/
class RCONAuthResponse extends RCONPacket {
/**
* Creates a RCON authentication response for the given request ID
*
* The request ID of the packet will match the client's request if
* authentication was successful
*
* @param int $requestId The request ID of the RCON connection
*/
public function __construct($requestId) {
parent::__construct($requestId, RCONPacket::SERVERDATA_AUTH_RESPONSE);
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/packets/rcon/RCONPacket.php';
/**
* This packet class represents a SERVERDATA_EXECCOMMAND packet sent to a
* Source server
*
* It is used to request a command execution on the server.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage rcon-packets
* @see SourceServer::rconExec()
*/
class RCONExecRequest extends RCONPacket {
/**
* Creates a RCON command execution request for the given request ID and
* command
*
* @param int $requestId The request ID of the RCON connection
* @param string $command The command to execute on the server
*/
public function __construct($requestId, $command) {
parent::__construct($requestId, RCONPacket::SERVERDATA_EXECCOMMAND, $command);
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/packets/rcon/RCONPacket.php';
/**
* This packet class represents a SERVERDATA_RESPONSE_VALUE packet sent by a
* Source server
*
* It is used to transport the output of a command from the server to the
* client which requested the command execution.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage rcon-packets
* @see SourceServer::rconExec()
*/
class RCONExecResponse extends RCONPacket {
/**
* Creates a RCON command response for the given request ID and command
* output
*
* @param int $requestId The request ID of the RCON connection
* @param string $commandResponse The output of the command executed on the
* server
*/
public function __construct($requestId, $commandResponse) {
parent::__construct($requestId, RCONPacket::SERVERDATA_RESPONSE_VALUE, $commandResponse);
}
/**
* Returns the output of the command execution
*
* @return string The output of the command
*/
public function getResponse() {
$response = $this->contentData->_array();
return substr($response, 0, strlen($response) - 2);
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/packets/SteamPacket.php';
/**
* This packet class represents a RCON request packet sent to a GoldSrc server
*
* It is used to request a command execution on the server.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage rcon-packets
* @see GoldSrcServer::rconExec()
*/
class RCONGoldSrcRequest extends SteamPacket {
/**
* Creates a request for the given request string
*
* The request string has the form <var>rcon {challenge number} {RCON
* password} {command}</var>.
*
* @param string $request The request string to send to the server
*/
public function __construct($request) {
parent::__construct(0x00, $request);
}
/**
* Returns the raw data representing this packet
*
* @return string A string containing the raw data of this request packet
*/
public function __toString() {
return pack('Va*', 0xFFFFFFFF, $this->contentData->_array());
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/packets/rcon/RCONPacket.php';
/**
* This packet class represents a RCON response packet sent by a GoldSrc server
*
* It is used to transport the output of a command from the server to the
* client which requested the command execution.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage rcon-packets
* @see GoldSrcServer::rconExec()
*/
class RCONGoldSrcResponse extends SteamPacket
{
/**
* Creates a RCON command response for the given command output
*
* @param string $commandResponse The output of the command executed on the
* server
*/
public function __construct($commandResponse) {
parent::__construct(SteamPacket::RCON_GOLDSRC_RESPONSE_HEADER, $commandResponse);
}
/**
* Returns the output of the command execution
*
* @return string The output of the command
*/
public function getResponse() {
return substr($this->contentData->_array(), 0, -2);
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/packets/SteamPacket.php';
/**
* This module is included by all classes representing a packet used by
* Source's RCON protocol
*
* It provides a basic implementation for initializing and serializing such a
* packet.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage rcon-packets
* @see RCONPacketFactory
*/
abstract class RCONPacket extends SteamPacket {
/**
* @var int Header for authentication requests
*/
const SERVERDATA_AUTH = 3;
/**
* @var int Header for replies to authentication attempts
*/
const SERVERDATA_AUTH_RESPONSE = 2;
/**
* @var int Header for command execution requests
*/
const SERVERDATA_EXECCOMMAND = 2;
/**
* @var int Header for packets with the output of a command execution
*/
const SERVERDATA_RESPONSE_VALUE = 0;
/**
* @var int The request ID used to identify the RCON communication
*/
private $requestId;
/**
* Creates a new RCON packet object with the given request ID, type and
* content data
*
* @param int $requestId The request ID for the current RCON communication
* @param int $rconHeader The header for the packet type
* @param string $rconData The raw packet data
*/
public function __construct($requestId, $rconHeader, $rconData = null) {
parent::__construct($rconHeader, "$rconData\0\0");
$this->requestId = $requestId;
}
/**
* Returns the request ID used to identify the RCON communication
*
* @return int The request ID used to identify the RCON communication
*/
public function getRequestId() {
return $this->requestId;
}
/**
* Returns the raw data representing this packet
*
* @return string A string containing the raw data of this RCON packet
*/
public function __toString() {
$contentData = $this->contentData->_array();
return pack('V3a*', strlen($contentData) + 8, $this->requestId, $this->headerData, $contentData);
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'ByteBuffer.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/SteamPacketFactory.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/rcon/RCONAuthResponse.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/rcon/RCONExecResponse.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/rcon/RCONPacket.php';
/**
* This module provides functionality to handle raw packet data for Source RCON
*
* It's is used to transform data bytes into packet objects for RCON
* communication with Source servers.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage rcon-packets
* @see RCONPacket
*/
abstract class RCONPacketFactory
{
/**
* Creates a new packet object based on the header byte of the given raw
* data
*
* @param string $rawData The raw data of the packet
* @return RCONPacket The packet object generated from the packet data
* @throws PacketFormatException if the packet header is not recognized
*/
public static function getPacketFromData($rawData) {
$byteBuffer = new ByteBuffer($rawData);
$requestId = $byteBuffer->getLong();
$header = $byteBuffer->getLong();
$data = $byteBuffer->getString();
switch($header) {
case RCONPacket::SERVERDATA_AUTH_RESPONSE:
return new RCONAuthResponse($requestId);
case RCONPacket::SERVERDATA_RESPONSE_VALUE:
return new RCONExecResponse($requestId, $data);
default:
throw new PacketFormatException('Unknown packet with header ' . dechex($header) . ' received.');
}
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/packets/rcon/RCONPacket.php';
/**
* This packet class represents a special SERVERDATA_RESPONSE_VALUE packet
* which is sent to the server
*
* It is used to determine the end of a RCON response from Source servers.
* Packets of this type are sent after the actual RCON command and the empty
* response packet from the server will indicate the end of the response.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage rcon-packets
* @see SourceServer::rconExec()
*/
class RCONTerminator extends RCONPacket {
/**
* Creates a new RCON terminator packet instance for the given request ID
*
* @param int $requestId The request ID for the current RCON communication
*/
public function __construct($requestId) {
parent::__construct($requestId, RCONPacket::SERVERDATA_RESPONSE_VALUE);
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/packets/SteamPacket.php';
/**
* This abstract class implements a method to generate raw packet data used by
* request packets which send a challenge number
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage packets
*/
abstract class RequestPacketWithChallenge extends SteamPacket {
/**
* Returns the raw data representing this packet
*
* @return string A string containing the raw data of this request packet
*/
public function __toString() {
return pack('cccccV', 0xFF, 0xFF, 0xFF, 0xFF, $this->headerData, $this->contentData->_array());
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2013, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/packets/S2A_INFO_BasePacket.php';
/**
* This class represents a S2A_INFO_DETAILED response packet sent by a Source
* or GoldSrc server
*
* Out-of-date (before 10/24/2008) GoldSrc servers use an older format (see
* {@link S2A_INFO_DETAILED_Packet}).
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage packets
* @see GameServer::updateServerInfo()
*/
class S2A_INFO2_Packet extends S2A_INFO_BasePacket {
const EDF_GAME_ID = 0x01;
const EDF_GAME_PORT = 0x80;
const EDF_SERVER_ID = 0x10;
const EDF_SERVER_TAGS = 0x20;
const EDF_SOURCE_TV = 0x40;
/**
* Creates a new S2A_INFO2 response object based on the given data
*
* @param string $data The raw packet data replied from the server
*/
public function __construct($data) {
parent::__construct(SteamPacket::S2A_INFO2_HEADER, $data);
$this->info['networkVersion'] = $this->contentData->getByte();
$this->info['serverName'] = $this->contentData->getString();
$this->info['mapName'] = $this->contentData->getString();
$this->info['gameDir'] = $this->contentData->getString();
$this->info['gameDesc'] = $this->contentData->getString();
$this->info['appId'] = $this->contentData->getShort();
$this->info['numberOfPlayers'] = $this->contentData->getByte();
$this->info['maxPlayers'] = $this->contentData->getByte();
$this->info['botNumber'] = $this->contentData->getByte();
$this->info['dedicated'] = chr($this->contentData->getByte());
$this->info['operatingSystem'] = chr($this->contentData->getByte());
$this->info['passwordProtected'] = $this->contentData->getByte() == 1;
$this->info['secureServer'] = $this->contentData->getByte() == 1;
$this->info['gameVersion'] = $this->contentData->getString();
if($this->contentData->remaining() > 0) {
$extraDataFlag = $this->contentData->getByte();
if ($extraDataFlag & self::EDF_GAME_PORT) {
$this->info['serverPort'] = $this->contentData->getShort();
}
if ($extraDataFlag & self::EDF_SERVER_ID) {
$this->info['serverId'] = $this->contentData->getUnsignedLong() | ($this->contentData->getUnsignedLong() << 32);
}
if ($extraDataFlag & self::EDF_SOURCE_TV) {
$this->info['tvPort'] = $this->contentData->getShort();
$this->info['tvName'] = $this->contentData->getString();
}
if ($extraDataFlag & self::EDF_SERVER_TAGS) {
$this->info['serverTags'] = $this->contentData->getString();
}
if ($extraDataFlag & self::EDF_GAME_ID) {
$this->info['gameId'] = $this->contentData->getUnsignedLong() | ($this->contentData->getUnsignedLong() << 32);
}
}
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2013, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/packets/SteamPacket.php';
/**
* This module implements methods to generate and access server information
* from S2A_INFO_DETAILED and S2A_INFO2 response packets
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage packets
* @see S2A_INFO_DETAILED_Packet
* @see S2A_INFO2_Packet
*/
abstract class S2A_INFO_BasePacket extends SteamPacket {
/**
* @var array
*/
protected $info = array();
/**
* Returns a generated array of server properties from the instance
* variables of the packet object
*
* @return array The information provided by the server
*/
public function getInfo() {
return $this->info;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2013, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/packets/S2A_INFO_BasePacket.php';
/**
* This class represents a S2A_INFO_DETAILED response packet sent by a GoldSrc
* server
*
* @author Sebastian Staudt
* @deprecated Only outdated GoldSrc servers (before 10/24/2008) use this
* format. Newer ones use the same format as Source servers now
* (see {@link S2A_INFO2_Packet}).
* @package steam-condenser
* @subpackage packets
* @see GameServer::updateServerInfo()
*/
class S2A_INFO_DETAILED_Packet extends S2A_INFO_BasePacket {
/**
* Creates a new S2A_INFO_DETAILED response object based on the given data
*
* @param string $data The raw packet data replied from the server
*/
public function __construct($data) {
parent::__construct(SteamPacket::S2A_INFO_DETAILED_HEADER, $data);
$this->info['serverIp'] = $this->contentData->getString();
$this->info['serverName'] = $this->contentData->getString();
$this->info['mapName'] = $this->contentData->getString();
$this->info['gameDir'] = $this->contentData->getString();
$this->info['gameDescription'] = $this->contentData->getString();
$this->info['numberOfPlayers'] = $this->contentData->getByte();
$this->info['maxPlayers'] = $this->contentData->getByte();
$this->info['networkVersion'] = $this->contentData->getByte();
$this->info['dedicated'] = $this->contentData->getByte();
$this->info['operatingSystem'] = $this->contentData->getByte();
$this->info['passwordProtected'] = $this->contentData->getByte() == 1;
$this->info['isMod'] = $this->contentData->getByte() == 1;
if($this->isMod) {
$this->info['modInfo']['urlInfo'] = $this->contentData->getString();
$this->info['modInfo']['urlDl'] = $this->contentData->getString();
$this->contentData->getByte();
if($this->contentData->remaining() == 12) {
$this->info['modInfo']['modVersion'] = $this->contentData->getLong();
$this->info['modInfo']['modSize'] = $this->contentData->getLong();
$this->info['modInfo']['svOnly'] = ($this->contentData->getByte() == 1);
$this->info['modInfo']['clDll'] = ($this->contentData->getByte() == 1);
$this->info['secure'] = $this->contentData->getByte() == 1;
$this->info['numberOfBots'] = $this->contentData->getByte();
}
} else {
$this->info['secure'] = $this->contentData->getByte() == 1;
$this->info['numberOfBots'] = $this->contentData->getByte();
}
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2010-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/packets/SteamPacket.php';
/**
* This class represents a S2A_LOGSTRING packet used to transfer log messages
*
* @package steam-condenser
* @subpackage packets
* @author Sebastian Staudt
*/
class S2A_LOGSTRING_Packet extends SteamPacket {
/**
* @var string The log message contained in this packet
*/
private $message;
/**
* Creates a new S2A_LOGSTRING object based on the given data
*
* @param string $data The raw packet data sent by the server
*/
public function __construct($data) {
parent::__construct(SteamPacket::S2A_LOGSTRING_HEADER, $data);
$this->contentData->getByte();
$this->message = $this->contentData->getString();
}
/**
* Returns the log message contained in this packet
*
* @return string The log message
*/
public function getMessage() {
return $this->message;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/SteamPlayer.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/SteamPacket.php';
/**
* This class represents a S2A_PLAYER response sent by a game server
*
* It is used to transfer a list of players currently playing on the server.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage packets
* @see GameServer::updatePlayerInfo()
*/
class S2A_PLAYER_Packet extends SteamPacket {
/**
* @var array
*/
private $playerHash;
/**
* Creates a new S2A_PLAYER response object based on the given data
*
* @param string $contentData The raw packet data sent by the server
* @throws PacketFormatException if the packet data is missing
*/
public function __construct($contentData) {
if (empty($contentData)) {
throw new PacketFormatException('Wrong formatted S2A_RULES packet.');
}
parent::__construct(SteamPacket::S2A_PLAYER_HEADER, $contentData);
$this->contentData->getByte();
$this->playerHash = array();
while($this->contentData->remaining() > 0) {
$playerData = array($this->contentData->getByte(), $this->contentData->getString(), $this->contentData->getLong(), $this->contentData->getFloat());
$this->playerHash[$playerData[1]] = new SteamPlayer($playerData[0], $playerData[1], $playerData[2], $playerData[3]);
}
}
/**
* Returns the list of active players provided by the server
*
* @return array All active players on the server
*/
public function getPlayerHash() {
return $this->playerHash;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2013, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/packets/SteamPacket.php';
/**
* This class represents a S2A_RULES response sent by a game server
*
* It is used to transfer a list of server rules (a.k.a. CVARs) with their
* active values.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage packets
* @see GameServer::updateRulesInfo()
*/
class S2A_RULES_Packet extends SteamPacket {
/**
* @var array
*/
private $rulesArray;
/**
* Creates a new S2A_RULES response object based on the given data
*
* @param string $contentData The raw packet data sent by the server
* @throws PacketFormatException if the packet data is missing
*/
public function __construct($contentData) {
if (empty($contentData)) {
throw new PacketFormatException('Wrong formatted S2A_RULES packet.');
}
parent::__construct(SteamPacket::S2A_RULES_HEADER, $contentData);
$rulesCount = $this->contentData->getShort();
$this->rulesArray = array();
for($x = 0; $x < $rulesCount; $x++) {
$rule = $this->contentData->getString();
$value = $this->contentData->getString();
if(empty($rule)) {
break;
}
$this->rulesArray[$rule] = $value;
}
}
/**
* Returns the list of server rules (a.k.a. CVars) with the current values
*
* @return array A list of server rules
*/
public function getRulesArray() {
return $this->rulesArray;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/packets/SteamPacket.php';
/**
* This packet class represents a S2C_CHALLENGE response replied by a game
* server
*
* It is used to provide a challenge number to a client requesting information
* from the game server.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage packets
* @see GameServer::updateChallengeNumber()
*/
class S2C_CHALLENGE_Packet extends SteamPacket {
/**
* Creates a new S2C_CHALLENGE response object based on the given data
*
* @param string $challengeNumber The raw packet data replied from the
* server
*/
public function __construct($challengeNumber) {
parent::__construct(SteamPacket::S2C_CHALLENGE_HEADER, $challengeNumber);
}
/**
* Returns the challenge number received from the game server
*
* @return int The challenge number provided by the game server
*/
public function getChallengeNumber() {
return $this->contentData->rewind()->getLong();
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2012, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'ByteBuffer.php';
/**
* This module implements the basic functionality used by most of the packets
* used in communication with master, Source or GoldSrc servers.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage packets
* @see SteamPacketFactory
*/
abstract class SteamPacket {
const S2A_INFO_DETAILED_HEADER = 0x6D;
const A2S_INFO_HEADER = 0x54;
const S2A_INFO2_HEADER = 0x49;
const A2S_PLAYER_HEADER = 0x55;
const S2A_PLAYER_HEADER = 0x44;
const A2S_RULES_HEADER = 0x56;
const S2A_RULES_HEADER = 0x45;
const A2S_SERVERQUERY_GETCHALLENGE_HEADER = 0x57;
const S2C_CHALLENGE_HEADER = 0x41;
const A2M_GET_SERVERS_BATCH2_HEADER = 0x31;
const C2M_CHECKMD5_HEADER = 0x4D;
const M2A_SERVER_BATCH_HEADER = 0x66;
const M2C_ISVALIDMD5_HEADER = 0x4E;
const M2S_REQUESTRESTART_HEADER = 0x4F;
const RCON_GOLDSRC_CHALLENGE_HEADER = 0x63;
const RCON_GOLDSRC_NO_CHALLENGE_HEADER = 0x39;
const RCON_GOLDSRC_RESPONSE_HEADER = 0x6C;
const S2A_LOGSTRING_HEADER = 0x52;
const S2M_HEARTBEAT2_HEADER = 0x30;
/**
* @var string This variable stores the content of the packet
*/
protected $contentData;
/**
* @var int This byte stores the type of the packet
*/
protected $headerData;
/**
* Creates a new packet object based on the given data
*
* @param int $headerData The packet header
* @param string $contentData The raw data of the packet
*/
public function __construct($headerData, $contentData = null) {
$this->headerData = $headerData;
$this->contentData = ByteBuffer::wrap($contentData);
}
/**
* @return ByteBuffer The data payload of the packet
*/
public function getData() {
return $this->contentData;
}
/**
* @return int The header of the packet
*/
public function getHeader() {
return $this->headerData;
}
/**
* Returns the raw data representing this packet
*
* @return string A string containing the raw data of this request packet
*/
public function __toString() {
$packetData = pack('c5', 0xFF, 0xFF, 0xFF, 0xFF, $this->headerData);
$packetData .= $this->contentData->_array();
return $packetData;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2013, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'exceptions/PacketFormatException.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/rcon/RCONGoldSrcResponse.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/S2A_INFO_DETAILED_Packet.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/A2S_INFO_Packet.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/S2A_INFO2_Packet.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/A2S_PLAYER_Packet.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/S2A_PLAYER_Packet.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/A2S_RULES_Packet.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/S2A_RULES_Packet.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/A2S_SERVERQUERY_GETCHALLENGE_Packet.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/S2C_CHALLENGE_Packet.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/A2M_GET_SERVERS_BATCH2_Packet.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/M2A_SERVER_BATCH_Packet.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/S2A_LOGSTRING_Packet.php';
/**
* This module provides functionality to handle raw packet data, including data
* split into several UDP / TCP packets and BZIP2 compressed data. It's the
* main utility to transform data bytes into packet objects.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage packets
* @see SteamPacket
*/
abstract class SteamPacketFactory {
/**
* Creates a new packet object based on the header byte of the given raw
* data
*
* @param string $rawData The raw data of the packet
* @throws PacketFormatException if the packet header is not recognized
* @return SteamPacket The packet object generated from the packet data
*/
public static function getPacketFromData($rawData) {
$header = ord($rawData[0]);
$data = substr($rawData, 1);
switch($header) {
case SteamPacket::S2A_INFO_DETAILED_HEADER:
return new S2A_INFO_DETAILED_Packet($data);
case SteamPacket::A2S_INFO_HEADER:
return new A2S_INFO_Packet();
case SteamPacket::S2A_INFO2_HEADER:
return new S2A_INFO2_Packet($data);
case SteamPacket::A2S_PLAYER_HEADER:
return new A2S_PLAYER_Packet();
case SteamPacket::S2A_PLAYER_HEADER:
return new S2A_PLAYER_Packet($data);
case SteamPacket::A2S_RULES_HEADER:
return new A2S_RULES_Packet();
case SteamPacket::S2A_RULES_HEADER:
return new S2A_RULES_Packet($data);
case SteamPacket::A2S_SERVERQUERY_GETCHALLENGE_HEADER:
return new A2S_SERVERQUERY_GETCHALLENGE_Packet();
case SteamPacket::S2C_CHALLENGE_HEADER:
return new S2C_CHALLENGE_Packet($data);
case SteamPacket::A2M_GET_SERVERS_BATCH2_HEADER:
return new A2M_GET_SERVERS_BATCH2_Packet($data);
case SteamPacket::M2A_SERVER_BATCH_HEADER:
return new M2A_SERVER_BATCH_Packet($data);
case SteamPacket::RCON_GOLDSRC_CHALLENGE_HEADER:
case SteamPacket::RCON_GOLDSRC_NO_CHALLENGE_HEADER:
case SteamPacket::RCON_GOLDSRC_RESPONSE_HEADER:
return new RCONGoldSrcResponse($data);
case SteamPacket::S2A_LOGSTRING_HEADER:
return new S2A_LOGSTRING_Packet($data);
default:
throw new PacketFormatException('Unknown packet with header 0x' . dechex($header) . ' received.');
}
}
/**
* Reassembles the data of a split and/or compressed packet into a single
* packet object
*
* @param array $splitPackets An array of packet data
* @param bool $isCompressed whether the data of this packet is compressed
* @param int $packetChecksum The CRC32 checksum of the decompressed
* packet data
* @throws PacketFormatException if the calculated CRC32 checksum does not
* match the expected value
* @return SteamPacket The reassembled packet
* @see packetFromData()
*/
public static function reassemblePacket($splitPackets, $isCompressed = false, $packetChecksum = 0) {
$packetData = '';
foreach($splitPackets as $splitPacket) {
$packetData .= $splitPacket;
}
if($isCompressed) {
$packetData = bzdecompress($packetData);
if(crc32($packetData) != $packetChecksum) {
throw new PacketFormatException('CRC32 checksum mismatch of uncompressed packet data.');
}
}
$packetData = substr($packetData, 4);
return self::getPacketFromData($packetData);
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2013, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'exceptions/SteamCondenserException.php';
require_once STEAM_CONDENSER_PATH . 'exceptions/TimeoutException.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/A2S_INFO_Packet.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/A2S_PLAYER_Packet.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/A2S_RULES_Packet.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/A2S_SERVERQUERY_GETCHALLENGE_Packet.php';
require_once STEAM_CONDENSER_PATH . 'steam/servers/Server.php';
/**
* This class is subclassed by classes representing different game server
* implementations and provides the basic functionality to communicate with
* them using the common query protocol
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage servers
*/
abstract class GameServer extends Server {
const REQUEST_CHALLENGE = 0;
const REQUEST_INFO = 1;
const REQUEST_PLAYER = 2;
const REQUEST_RULES = 3;
/**
* @var Monolog\Logger The Monolog logger for this class
*/
private static $log;
/**
* @var int The challenge number to communicate with the server
*/
protected $challengeNumber;
/**
* @var array Basic information about this server
*/
protected $infoHash;
/**
* @var int The response time of this server
*/
protected $ping;
/**
* @var array The players playing on this server
*/
protected $playerHash;
/**
* @var bool whether the RCON connection is already authenticated
*/
protected $rconAuthenticated;
/**
* @var array The settings applied on the server
*/
protected $rulesHash;
/**
* @var SteamSocket The socket of to communicate with the server
*/
protected $socket;
/**
* Parses the player attribute names supplied by <var>rcon status</var>
*
* @param string $statusHeader The header line provided by <var>rcon
* status</var>
* @return array Split player attribute names
* @see splitPlayerStatus()
*/
protected function getPlayerStatusAttributes($statusHeader) {
$statusAttributes = array();
foreach(preg_split("/\s+/", $statusHeader) as $attribute) {
if($attribute == 'connected') {
$statusAttributes[] = 'time';
} else if($attribute == 'frag') {
$statusAttributes[] = 'score';
} else {
$statusAttributes[] = $attribute;
}
}
return $statusAttributes;
}
/**
* Splits the player status obtained with <var>rcon status</var>
*
* @param array $attributes The attribute names
* @param string $playerStatus The status line of a single player
* @return array The attributes with the corresponding values for this
* player
* @see getPlayerStatusAttributes()
*/
protected function splitPlayerStatus($attributes, $playerStatus) {
if($attributes[0] != 'userid') {
$playerStatus = preg_replace('/^\d+ +/', '', $playerStatus);
}
$firstQuote = strpos($playerStatus, '"');
$lastQuote = strrpos($playerStatus, '"');
$data = array(
substr($playerStatus, 0, $firstQuote),
substr($playerStatus, $firstQuote + 1, $lastQuote - 1 - $firstQuote),
substr($playerStatus, $lastQuote + 1)
);
$data = array_merge(
array_filter(preg_split("/\s+/", trim($data[0]))),
array($data[1]),
preg_split("/\s+/", trim($data[2]))
);
$data = array_values($data);
if(sizeof($attributes) > sizeof($data) &&
in_array('state', $attributes)) {
array_splice($data, 3, 0, array(null, null, null));
} elseif(sizeof($attributes) < sizeof($data)) {
unset($data[1]);
$data = array_values($data);
}
$playerData = array();
for($i = 0; $i < sizeof($data); $i ++) {
$playerData[$attributes[$i]] = $data[$i];
}
return $playerData;
}
/**
* Creates a new instance of a game server object
*
* @param string $address Either an IP address, a DNS name or one of them
* combined with the port number. If a port number is given, e.g.
* 'server.example.com:27016' it will override the second argument.
* @param int $port The port the server is listening on
* @throws SteamCondenserException if an host name cannot be resolved
*/
public function __construct($address, $port = 27015) {
parent::__construct($address, $port);
$this->rconAuthenticated = false;
if (!isset(self::$log)) {
//self::$log = new \Monolog\Logger('MasterServer');
}
}
/**
* Returns the last measured response time of this server
*
* If the latency hasn't been measured yet, it is done when calling this
* method for the first time.
*
* If this information is vital to you, be sure to call
* {@link updatePing()} regularly to stay up-to-date.
*
* @return int The latency of this server in milliseconds
* @see updatePing()
*/
public function getPing() {
if($this->ping == null) {
$this->updatePing();
}
return $this->ping;
}
/**
* Returns a list of players currently playing on this server
*
* If the players haven't been fetched yet, it is done when calling this
* method for the first time.
*
* As the players and their scores change quite often be sure to update
* this list regularly by calling {@link updatePlayers()} if you rely on
* this information.
*
* @param string $rconPassword The RCON password of this server may be
* provided to gather more detailed information on the players, like
* STEAM_IDs.
* @return array The players on this server
* @see updatePlayers()
*/
public function getPlayers($rconPassword = null) {
if($this->playerHash == null) {
$this->updatePlayers($rconPassword);
}
return $this->playerHash;
}
/**
* Returns the settings applied on the server. These settings are also
* called rules.
*
* If the rules haven't been fetched yet, it is done when calling this
* method for the first time.
*
* As the rules usually don't change often, there's almost no need to
* update this hash. But if you need to, you can achieve this by calling
* {@link updateRules()}.
*
* @return array The currently active server rules
* @see updateRules()
*/
public function getRules() {
if($this->rulesHash == null) {
$this->updateRules();
}
return $this->rulesHash;
}
/**
* Returns an associative array with basic information on the server.
*
* If the server information haven't been fetched yet, it is done when
* calling this method for the first time.
*
* The server information usually only changes on map change and when
* players join or leave. As the latter changes can be monitored by calling
* {@link updatePlayers()}, there's no need to call
* {@link updateServerInfo()} very often.
*
* @return array Server attributes with their values
* @see updateServerInfo()
*/
public function getServerInfo() {
if($this->infoHash == null) {
$this->updateServerInfo();
}
return $this->infoHash;
}
/**
* Initializes this server object with basic information
*
* @see updateChallengeNumber()
* @see updatePing()
* @see updateServerInfo()
*/
public function initialize() {
$this->updatePing();
$this->updateServerInfo();
$this->updateChallengeNumber();
}
/**
* Receives a response from the server
*
* @return SteamPacket The response packet replied by the server
*/
protected function getReply() {
return $this->socket->getReply();
}
/**
* Sends the specified request to the server and handles the returned
* response
*
* Depending on the given request type this will fill the various data
* attributes of the server object.
*
* @param int $requestType The type of request to send to the server
* @param bool $repeatOnFailure Whether the request should be repeated, if
* the replied packet isn't expected. This is useful to handle
* missing challenge numbers, which will be automatically filled in,
* although not requested explicitly.
* @throws SteamCondenserException if either the request type or the
* response packet is not known
*/
protected function handleResponseForRequest($requestType, $repeatOnFailure = true) {
switch($requestType) {
case self::REQUEST_CHALLENGE:
$expectedResponse = 'S2C_CHALLENGE_Packet';
$requestPacket = new A2S_PLAYER_Packet();
break;
case self::REQUEST_INFO:
$expectedResponse = 'S2A_INFO_BasePacket';
$requestPacket = new A2S_INFO_Packet();
break;
case self::REQUEST_PLAYER:
$expectedResponse = 'S2A_PLAYER_Packet';
$requestPacket = new A2S_PLAYER_Packet($this->challengeNumber);
break;
case self::REQUEST_RULES:
$expectedResponse = 'S2A_RULES_Packet';
$requestPacket = new A2S_RULES_Packet($this->challengeNumber);
break;
default:
throw new SteamCondenserException('Called with wrong request type.');
}
$this->sendRequest($requestPacket);
$responsePacket = $this->getReply();
if($responsePacket instanceof S2A_INFO_BasePacket) {
$this->infoHash = $responsePacket->getInfo();
} elseif($responsePacket instanceof S2A_PLAYER_Packet) {
$this->playerHash = $responsePacket->getPlayerHash();
} elseif($responsePacket instanceof S2A_RULES_Packet) {
$this->rulesHash = $responsePacket->getRulesArray();
} elseif($responsePacket instanceof S2C_CHALLENGE_Packet) {
$this->challengeNumber = $responsePacket->getChallengeNumber();
} else {
throw new SteamCondenserException('Response of type ' . get_class($responsePacket) . ' cannot be handled by this method.');
}
if(!($responsePacket instanceof $expectedResponse)) {
//self::$log->addInfo("Expected {$expectedResponse}, got " . get_class($responsePacket) . '.');
if($repeatOnFailure) {
$this->handleResponseForRequest($requestType, false);
}
}
}
/**
* Returns whether the RCON connection to this server is already
* authenticated
*
* @return bool <var>true</var> if the RCON connection is authenticated
* @see rconAuth()
*/
public function isRconAuthenticated() {
return $this->rconAuthenticated;
}
/**
* Authenticates the connection for RCON communication with the server
*
* @param string $password The RCON password of the server
* @return bool whether authentication was successful
* @see rconAuth()
* @throws SteamCondenserException if a problem occurs while parsing the
* reply
* @throws TimeoutException if the request times out
*/
abstract public function rconAuth($password);
/**
* Remotely executes a command on the server via RCON
*
* @param string $command The command to execute on the server via RCON
* @return string The output of the executed command
* @see rconExec()
* @throws SteamCondenserException if a problem occurs while parsing the
* reply
* @throws TimeoutException if the request times out
*/
abstract public function rconExec($command);
/**
* Sends a request packet to the server
*
* @param SteamPacket $requestData The request packet to send to the server
*/
protected function sendRequest(SteamPacket $requestData) {
$this->socket->send($requestData);
}
/**
* Sends a A2S_SERVERQUERY_GETCHALLENGE request to the server and updates
* the challenge number used to communicate with this server
*
* There's usually no need to call this method explicitly, because
* {@link handleResponseForRequest()} will automatically get the challenge
* number when the server assigns a new one.
*
* @see handleResponseForRequest()
* @see initialize()
*/
public function updateChallengeNumber() {
$this->handleResponseForRequest(self::REQUEST_CHALLENGE);
}
/**
* Sends a A2S_INFO request to the server and measures the time needed for
* the reply
*
* If this information is vital to you, be sure to call this method
* regularly to stay up-to-date.
*
* @return int The latency of this server in milliseconds
* @see getPing()
* @see initialize()
*/
public function updatePing() {
$this->sendRequest(new A2S_INFO_Packet());
$startTime = microtime(true);
$this->getReply();
$endTime = microtime(true);
$this->ping = intval(round(($endTime - $startTime) * 1000));
return $this->ping;
}
/**
* Sends a A2S_PLAYERS request to the server and updates the players' data
* for this server
*
* As the players and their scores change quite often be sure to update
* this list regularly by calling this method if you rely on this
* information.
*
* @param string $rconPassword The RCON password of this server may be
* provided to gather more detailed information on the players, like
* STEAM_IDs.
* @see getPlayers()
* @see handleResponseForRequest()
*/
public function updatePlayers($rconPassword = null) {
$this->handleResponseForRequest(self::REQUEST_PLAYER);
if(!$this->rconAuthenticated) {
if($rconPassword == null) {
return;
}
$this->rconAuth($rconPassword);
}
$players = array();
foreach(explode("\n", $this->rconExec('status')) as $line) {
if(strpos($line, '#') === 0 && $line != '#end') {
$players[] = trim(substr($line, 1));
}
}
$attributes = $this->getPlayerStatusAttributes(array_shift($players));
foreach($players as $player) {
$playerData = $this->splitPlayerStatus($attributes, $player);
if(array_key_exists($playerData['name'], $this->playerHash)) {
$this->playerHash[$playerData['name']]->addInformation($playerData);
}
}
}
/**
* Sends a A2S_RULES request to the server and updates the rules of this
* server
*
* As the rules usually don't change often, there's almost no need to
* update this hash. But if you need to, you can achieve this by calling
* this method.
*
* @see getRules()
* @see handleResponseForRequest()
*/
public function updateRules() {
$this->handleResponseForRequest(self::REQUEST_RULES);
}
/**
* Sends a A2S_INFO request to the server and updates this server's basic
* information
*
* The server information usually only changes on map change and when
* players join or leave. As the latter changes can be monitored by calling
* {@link updatePlayers()}, there's no need to call this method very often.
*
* @see getServerInfo()
* @see handleResponseForRequest()
* @see initialize()
*/
public function updateServerInfo() {
$this->handleResponseForRequest(self::REQUEST_INFO);
}
/**
* Returns a human-readable text representation of the server
*
* @return string Available information about the server in a
* human-readable format
*/
public function __toString() {
$returnString = '';
$returnString .= "Ping: {$this->ping}\n";
$returnString .= "Challenge number: {$this->challengeNumber}\n";
if($this->infoHash != null) {
$returnString .= "Info:\n";
foreach($this->infoHash as $key => $value) {
if(is_array($value)) {
$returnString .= " {$key}:\n";
foreach($value as $subKey => $subValue) {
$returnString .= " {$subKey} = {$subValue}\n";
}
} else {
$returnString .= " {$key}: {$value}\n";
}
}
}
if($this->playerHash != null) {
$returnString .= "Players:\n";
foreach($this->playerHash as $player) {
$returnString .= " {$player}\n";
}
}
if($this->rulesHash != null) {
$returnString .= "Rules:\n";
foreach($this->rulesHash as $key => $value) {
$returnString .= " {$key}: {$value}\n";
}
}
return $returnString;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2012, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/servers/GameServer.php';
require_once STEAM_CONDENSER_PATH . 'steam/servers/MasterServer.php';
require_once STEAM_CONDENSER_PATH . 'steam/sockets/GoldSrcSocket.php';
/**
* This class represents a GoldSrc game server and can be used to query
* information about and remotely execute commands via RCON on the server
*
* A GoldSrc game server is an instance of the Half-Life Dedicated Server
* (HLDS) running games using Valve's GoldSrc engine, like Half-Life
* Deathmatch, Counter-Strike 1.6 or Team Fortress Classic.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage servers
* @see SourceServer
*/
class GoldSrcServer extends GameServer {
/**
* @var bool
*/
protected $isHLTV;
/**
* @var string
*/
protected $rconPassword;
/**
* Returns a master server instance for the default master server for
* GoldSrc games
*
* @return MasterServer The GoldSrc master server
*/
public static function getMaster() {
return new MasterServer(MasterServer::GOLDSRC_MASTER_SERVER);
}
/**
* Creates a new instance of a GoldSrc server object
*
* @param string $address Either an IP address, a DNS name or one of them
* combined with the port number. If a port number is given, e.g.
* 'server.example.com:27016' it will override the second argument.
* @param int $port The port the server is listening on
* @param bool $isHLTV HLTV servers need special treatment, so this is used
* to determine if the server is a HLTV server
* @throws SteamCondenserException if an host name cannot be resolved
*/
public function __construct($address, $port = 27015, $isHLTV = false) {
parent::__construct($address, $port);
$this->isHLTV = $isHLTV;
}
/**
* Initializes the sockets to communicate with the GoldSrc server
*
* @see GoldSrcSocket
*/
public function initSocket() {
$this->socket = new GoldSrcSocket($this->ipAddress, $this->port, $this->isHLTV);
}
/**
* Saves the password for authenticating the RCON communication with the
* server
*
* @param string $password The RCON password of the server
* @return bool GoldSrc's RCON does not preauthenticate connections so
* this method always returns <var>true</var>
* @see rconAuth()
*/
public function rconAuth($password) {
$this->rconPassword = $password;
return true;
}
/**
* Remotely executes a command on the server via RCON
*
* @param string $command The command to execute on the server via RCON
* @return string The output of the executed command
* @see rconExec()
* @throws SteamCondenserException if a problem occurs while parsing the
* reply
* @throws TimeoutException if the request times out
*/
public function rconExec($command) {
return trim($this->socket->rconExec($this->rconPassword, $command));
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2013, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/packets/A2M_GET_SERVERS_BATCH2_Packet.php';
require_once STEAM_CONDENSER_PATH . 'steam/servers/Server.php';
require_once STEAM_CONDENSER_PATH . 'steam/sockets/MasterServerSocket.php';
/**
* This class represents a Steam master server and can be used to get game
* servers which are publicly available
*
* An intance of this class can be used much like Steam's server browser to get
* a list of available game servers, including filters to narrow down the
* search results.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage servers
*/
class MasterServer extends Server {
/**
* @var Monolog\Logger The Monolog logger for this class
*/
private static $log;
/**
* @var string The master server address to query for GoldSrc game servers
*/
const GOLDSRC_MASTER_SERVER = 'hl1master.steampowered.com:27011';
/**
* @var string The master server address to query for GoldSrc game servers
*/
const SOURCE_MASTER_SERVER = 'hl2master.steampowered.com:27011';
/**
* @var int The region code for the US east coast
*/
const REGION_US_EAST_COAST = 0x00;
/**
* @var int The region code for the US west coast
*/
const REGION_US_WEST_COAST = 0x01;
/**
* @var int The region code for South America
*/
const REGION_SOUTH_AMERICA = 0x02;
/**
* @var int The region code for Europe
*/
const REGION_EUROPE = 0x03;
/**
* @var int The region code for Asia
*/
const REGION_ASIA = 0x04;
/**
* @var int The region code for Australia
*/
const REGION_AUSTRALIA = 0x05;
/**
* @var int The region code for the Middle East
*/
const REGION_MIDDLE_EAST = 0x06;
/**
* @var int The region code for Africa
*/
const REGION_AFRICA = 0x07;
/**
* @var int The region code for the whole world
*/
const REGION_ALL = 0xFF;
/**
* @var int
*/
private static $retries = 3;
/**
* @var MasterServerSocket
*/
protected $socket;
/**
* Creates a new master server instance with the given address and port
*
* @param string $address Either an IP address, a DNS name or one of them
* combined with the port number. If a port number is given, e.g.
* 'server.example.com:27016' it will override the second argument.
* @param int $port The port the server is listening on
* @throws SteamCondenserException if an host name cannot be resolved
*/
public function __construct($address, $port = null) {
parent::__construct($address, $port);
if (!isset(self::$log)) {
self::$log = new \Monolog\Logger('MasterServer');
}
}
/**
* Sets the number of consecutive requests that may fail, before getting
* the server list is cancelled (default: 3)
*
* @param int $retries The number of allowed retries
*/
public static function setRetries($retries) {
self::$retries = $retries;
}
/**
* Returns a list of game server matching the given region and filters
*
* Filtering:
* Instead of filtering the results sent by the master server locally, you
* should at least use the following filters to narrow down the results
* sent by the master server.
*
* <b>Note:</b> Receiving all servers from the master server is taking
* quite some time.
*
* Available filters:
*
* <ul>
* <li><var>\type\d</var>: Request only dedicated servers
* <li><var>\secure\1</var>: Request only secure servers
* <li><var>\gamedir\[mod]</var>: Request only servers of a specific mod
* <li><var>\map\[mapname]</var>: Request only servers running a specific
* map
* <li><var>\linux\1</var>: Request only linux servers
* <li><var>\emtpy\1</var>: Request only **non**-empty servers
* <li><var>\full\1</var>: Request only servers **not** full
* <li><var>\proxy\1</var>: Request only spectator proxy servers
* </ul>
*
* @param int $regionCode The region code to specify a location of the
* game servers
* @param string $filter The filters that game servers should match
* @param bool $force Return a list of servers even if an error occured
* while fetching them from the master server
* @return array A list of game servers matching the given
* region and filters
* @see setTimeout()
* @see A2M_GET_SERVERS_BATCH2_Packet
* @throws SteamCondenserException if a problem occurs while parsing the
* reply
* @throws TimeoutException if too many timeouts occur while querying the
* master server
*/
public function getServers($regionCode = MasterServer::REGION_ALL , $filter = '', $force = false) {
$failCount = 0;
$finished = false;
$portNumber = 0;
$hostName = '0.0.0.0';
$serverArray = array();
while(true) {
$failCount = 0;
try {
do {
$this->socket->send(new A2M_GET_SERVERS_BATCH2_Packet($regionCode, "$hostName:$portNumber", $filter));
try {
$serverStringArray = $this->socket->getReply()->getServers();
foreach($serverStringArray as $serverString) {
$serverString = explode(':', $serverString);
$hostName = $serverString[0];
$portNumber = $serverString[1];
if($hostName != '0.0.0.0' && $portNumber != 0) {
$serverArray[] = array($hostName, $portNumber);
} else {
$finished = true;
}
}
$failCount = 0;
} catch(TimeoutException $e) {
$failCount ++;
if($failCount == self::$retries) {
throw $e;
}
self::$log->addInfo("Request to master server {$this->ipAddress} timed out, retrying...");
}
} while(!$finished);
break;
} catch(TimeoutException $e) {
if ($force) {
break;
} else if($this->rotateIp()) {
throw $e;
}
self::$log->addInfo("Request to master server failed, retrying {$this->ipAddress}...");
}
}
return $serverArray;
}
/**
* Initializes the socket to communicate with the master server
*
* @see MasterServerSocket
*/
public function initSocket() {
$this->socket = new MasterServerSocket($this->ipAddress, $this->port);
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2011-2012, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
/**
* This class is subclassed by all classes implementing server functionality
*
* It provides basic name resolution features and the ability to rotate
* between different IP addresses belonging to a single DNS name.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage servers
*/
abstract class Server {
/**
* @var array The currently selected IP address of this server
*/
protected $ipAddress;
/**
* @var array The IP addresses of this server
*/
protected $ipAddresses;
/**
* @var int The index of the currently selected IP address
*/
protected $ipIndex;
/**
* @var int The port of this server
*/
protected $port;
/**
* Creates a new server instance with the given address and port
*
* @param string $address Either an IP address, a DNS name or one of them
* combined with the port number. If a port number is given, e.g.
* 'server.example.com:27016' it will override the second argument.
* @param int $port The port the server is listening on
* @see initSocket()
* @throws SteamCondenserException if an host name cannot be resolved
*/
public function __construct($address, $port = null) {
$address = strval($address);
if(strpos($address, ':') !== false) {
$address = explode(':', $address, 2);
$port = $address[1];
$address = $address[0];
}
$this->ipAddresses = array();
$this->ipIndex = 0;
$this->port = intval($port);
$addresses = gethostbynamel($address);
if(empty($addresses)) {
throw new SteamCondenserException("Cannot resolve $address");
}
foreach($addresses as $address) {
$this->ipAddresses[] = $address;
}
$this->ipAddress = $this->ipAddresses[0];
$this->initSocket();
}
/**
* Disconnect the connections to this server
*
* <i>Note:
* In the base implementation this does nothing, only connection-based
* communication channels have to be disconnected.</i>
*/
public function disconnect() {}
/**
* Rotate this server's IP address to the next one in the IP list
*
* If this method returns <var>true</var>, it indicates that all IP
* addresses have been used, hinting at the server(s) being unreachable. An
* appropriate action should be taken to inform the user.
*
* Servers with only one IP address will always cause this method to return
* <var>true</var> and the sockets will not be reinitialized.
*
* @return bool <var>true</var>, if the IP list reached its end. If the
* list contains only one IP address, this method will instantly
* return <var>true</var>
* @see initSocket()
*/
public function rotateIp() {
if(sizeof($this->ipAddresses) == 1) {
return true;
}
$this->ipIndex = ($this->ipIndex + 1) % sizeof($this->ipAddresses);
$this->ipAddress = $this->ipAddresses[$this->ipIndex];
$this->initSocket();
return $this->ipIndex == 0;
}
/**
* Disconnects the connections to this server
*
* @see disconnect()
*/
public function __destruct() {
$this->disconnect();
}
/**
* Initializes the socket(s) to communicate with the server
*
* Must be implemented in subclasses to prepare sockets for server
* communication
*/
protected abstract function initSocket();
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2013, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'exceptions/RCONNoAuthException.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/rcon/RCONAuthRequest.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/rcon/RCONAuthResponse.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/rcon/RCONExecRequest.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/rcon/RCONTerminator.php';
require_once STEAM_CONDENSER_PATH . 'steam/servers/GameServer.php';
require_once STEAM_CONDENSER_PATH . 'steam/servers/MasterServer.php';
require_once STEAM_CONDENSER_PATH . 'steam/sockets/RCONSocket.php';
require_once STEAM_CONDENSER_PATH . 'steam/sockets/SourceSocket.php';
/**
* This class represents a Source game server and can be used to query
* information about and remotely execute commands via RCON on the server
*
* A Source game server is an instance of the Source Dedicated Server (SrcDS)
* running games using Valve's Source engine, like Counter-Strike: Source,
* Team Fortress 2 or Left4Dead.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage servers
* @see GoldSrcServer
*/
class SourceServer extends GameServer {
/**
* @var bool Whether the RCON connection is already authenticated
*/
protected $rconAuthenticated;
/**
* @var int The request ID used for RCON request
*/
protected $rconRequestId;
/**
* @var RCONSocket The TCP socket to use for RCON communication
*/
protected $rconSocket;
/**
* Disconnects the TCP-based channel used for RCON commands
*
* @see RCONSocket::close()
*/
public function disconnect() {
$this->rconSocket->close();
}
/**
* Returns a master server instance for the default master server for
* Source games
*
* @return MasterServer The Source master server
*/
public static function getMaster() {
return new MasterServer(MasterServer::SOURCE_MASTER_SERVER);
}
/**
* Returns a random 16-bit integer used to identify RCON communication
* packets
*
* @return int The request ID for RCON communication
*/
protected function generateRconRequestId() {
return rand(0, pow(2, 16));
}
/**
* Initializes the sockets to communicate with the Source server
*
* @see RCONSocket
* @see SourceSocket
*/
public function initSocket() {
$this->rconSocket = new RCONSocket($this->ipAddress, $this->port);
$this->socket = new SourceSocket($this->ipAddress, $this->port);
}
/**
* Authenticates the connection for RCON communication with the server
*
* @param string $password The RCON password of the server
* @return bool whether authentication was successful
* @see rconExec()
* @throws SteamCondenserException if a problem occurs while parsing the
* reply
* @throws TimeoutException if the request times out
*/
public function rconAuth($password) {
$this->rconRequestId = $this->generateRconRequestId();
$this->rconSocket->send(new RCONAuthRequest($this->rconRequestId, $password));
try {
$this->rconSocket->getReply();
$reply = $this->rconSocket->getReply();
$this->rconAuthenticated = $reply->getRequestId() == $this->rconRequestId;
} catch (SocketException $e) {
if (defined('SOCKET_ECONNRESET') &&
$e->getCode() == SOCKET_ECONNRESET) {
throw new RCONBanException();
} else {
throw $e;
}
}
return $this->rconAuthenticated;
}
/**
* Remotely executes a command on the server via RCON
*
* @param string $command The command to execute on the server via RCON
* @return string The output of the executed command
* @see rconAuth()
* @throws RCONBanException if banned by the server
* @throws RCONNoAuthException if not authenticated with the server
* @throws SteamCondenserException if a problem occurs while parsing the
* reply
* @throws TimeoutException if the request times out
*/
public function rconExec($command) {
if(!$this->rconAuthenticated) {
throw new RCONNoAuthException();
}
$this->rconSocket->send(new RCONExecRequest($this->rconRequestId, $command));
$isMulti = false;
$response = array();
do {
try {
$responsePacket = $this->rconSocket->getReply();
if ($responsePacket instanceof RCONAuthResponse) {
$this->rconAuthenticated = false;
throw new RCONNoAuthException();
}
if (!$isMulti && strlen($responsePacket->getResponse()) > 0) {
$isMulti = true;
$this->rconSocket->send(new RCONTerminator($this->rconRequestId));
}
} catch (SocketException $e) {
if (defined('SOCKET_ECONNRESET') &&
$e->getCode() == SOCKET_ECONNRESET) {
$this->rconAuthenticated = false;
throw new RCONNoAuthException();
} else {
throw $e;
}
}
$response[] = $responsePacket->getResponse();
} while($isMulti && !(empty($response[sizeof($response) - 2]) && empty($response[sizeof($response) - 1])));
return trim(join('', $response));
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'exceptions/RCONBanException.php';
require_once STEAM_CONDENSER_PATH . 'exceptions/RCONNoAuthException.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/SteamPacket.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/rcon/RCONGoldSrcRequest.php';
require_once STEAM_CONDENSER_PATH . 'steam/sockets/SteamSocket.php';
/**
* This class represents a socket used to communicate with game servers based
* on the GoldSrc engine (e.g. Half-Life, Counter-Strike)
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage sockets
*/
class GoldSrcSocket extends SteamSocket {
/**
* @var Monolog\Logger The Monolog logger for this class
*/
private static $log;
/**
* @var boolean
*/
private $isHLTV;
/**
* @var long
*/
private $rconChallenge = -1;
/**
* Creates a new socket to communicate with the server on the given IP
* address and port
*
* @param string $ipAddress Either the IP address or the DNS name of the
* server
* @param int $portNumber The port the server is listening on
* @param bool $isHLTV <var>true</var> if the target server is a HTLV
* instance. HLTV behaves slightly different for RCON commands, this
* flag increases compatibility.
*/
public function __construct($ipAddress, $portNumber = 27015, $isHLTV = false) {
parent::__construct($ipAddress, $portNumber);
$this->isHLTV = $isHLTV;
if (!isset(self::$log)) {
self::$log = new \Monolog\Logger('GoldSrcSocket');
}
}
/**
* Reads a packet from the socket
*
* The Source query protocol specifies a maximum packet size of 1,400
* bytes. Bigger packets will be split over several UDP packets. This
* method reassembles split packets into single packet objects.
*
* @return SteamPacket The packet replied from the server
*/
public function getReply() {
$bytesRead = $this->receivePacket(1400);
if($this->buffer->getLong() == -2) {
do {
$requestId = $this->buffer->getLong();
$packetCountAndNumber = $this->buffer->getByte();
$packetCount = $packetCountAndNumber & 0xF;
$packetNumber = ($packetCountAndNumber >> 4) + 1;
$splitPackets[$packetNumber - 1] = $this->buffer->get();
self::$log->addDebug("Received packet $packetNumber of $packetCount for request #$requestId");
if(sizeof($splitPackets) < $packetCount) {
try {
$bytesRead = $this->receivePacket();
} catch(TimeoutException $e) {
$bytesRead = 0;
}
} else {
$bytesRead = 0;
}
} while($bytesRead > 0 && $this->buffer->getLong() == -2);
$packet = SteamPacketFactory::reassemblePacket($splitPackets);
} else {
$packet = SteamPacketFactory::getPacketFromData($this->buffer->get());
}
self::$log->addDebug("Received packet of type \"" . get_class($packet) . "\"");
return $packet;
}
/**
* Executes the given command on the server via RCON
*
* @param string $password The password to authenticate with the server
* @param string $command The command to execute on the server
* @return RCONGoldSrcResponse The response replied by the server
* @see rconChallenge()
* @see rconSend()
* @throws RCONBanException if the IP of the local machine has been banned
* on the game server
* @throws RCONNoAuthException if the password is incorrect
*/
public function rconExec($password, $command) {
if($this->rconChallenge == -1 || $this->isHLTV) {
$this->rconGetChallenge();
}
$this->rconSend("rcon {$this->rconChallenge} $password $command");
$this->rconSend("rcon {$this->rconChallenge} $password");
if($this->isHLTV) {
try {
$response = $this->getReply()->getResponse();
} catch(TimeoutException $e) {
$response = '';
}
} else {
$response = $this->getReply()->getResponse();
}
if(trim($response) == 'Bad rcon_password.') {
throw new RCONNoAuthException();
} elseif(trim($response) == 'You have been banned from this server.') {
throw new RCONBanException();
}
do {
$responsePart = $this->getReply()->getResponse();
$response .= $responsePart;
} while(strlen($responsePart) > 0);
return $response;
}
/**
* Requests a challenge number from the server to be used for further
* requests
*
* @throws RCONBanException if the IP of the local machine has been banned
* on the game server
* @see rconSend()
*/
public function rconGetChallenge() {
$this->rconSend('challenge rcon');
$response = trim($this->getReply()->getResponse());
if($response == 'You have been banned from this server.') {
throw new RCONBanException();
}
$this->rconChallenge = floatval(substr($response, 14));
}
/**
* Wraps the given command in a RCON request packet and send it to the
* server
*
* @param string $command The RCON command to send to the server
*/
public function rconSend($command) {
$this->send(new RCONGoldSrcRequest($command));
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2013, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'exceptions/PacketFormatException.php';
require_once STEAM_CONDENSER_PATH . 'steam/sockets/SteamSocket.php';
/**
* This class represents a socket used to communicate with master servers
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage sockets
*/
class MasterServerSocket extends SteamSocket {
/**
* @var Monolog\Logger The Monolog logger for this class
*/
private static $log;
/**
* Creates a new UDP socket to communicate with the server on the given IP
* address and port
*
* @param string $ipAddress Either the IP address or the DNS name of the
* server
* @param int $portNumber The port the server is listening on
*/
public function __construct($ipAddress, $portNumber = 27015) {
parent::__construct($ipAddress, $portNumber);
if (!isset(self::$log)) {
self::$log = new \Monolog\Logger('MasterServerSocket');
}
}
/**
* Reads a single packet from the socket
*
* @return SteamPacket The packet replied from the server
* @throws PacketFormatException if the packet has the wrong format
*/
public function getReply() {
$this->receivePacket(1500);
if($this->buffer->getLong() != -1) {
throw new PacketFormatException("Master query response has wrong packet header.");
}
$packet = SteamPacketFactory::getPacketFromData($this->buffer->get());
self::$log->addDebug("Received reply of type \"" . get_class($packet) . "\"");
return $packet;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2013, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'TCPSocket.php';
require_once STEAM_CONDENSER_PATH . 'exceptions/RCONBanException.php';
require_once STEAM_CONDENSER_PATH . 'exceptions/RCONNoAuthException.php';
require_once STEAM_CONDENSER_PATH . 'exceptions/PacketFormatException.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/rcon/RCONPacket.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/rcon/RCONPacketFactory.php';
require_once STEAM_CONDENSER_PATH . 'steam/sockets/SteamSocket.php';
/**
* This class represents a socket used for RCON communication with game servers
* based on the Source engine (e.g. Team Fortress 2, Counter-Strike: Source)
*
* The Source engine uses a stateful TCP connection for RCON communication and
* uses an additional socket of this type to handle RCON requests.
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage sockets
*/
class RCONSocket extends SteamSocket {
/**
* @var Monolog\Logger The Monolog logger for this class
*/
private static $log;
/**
* @var string
*/
private $ipAddress;
/**
* @var int
*/
private $portNumber;
/**
* Creates a new TCP socket to communicate with the server on the given IP
* address and port
*
* @param string $ipAddress Either the IP address or the DNS name of the
* server
* @param int $portNumber The port the server is listening on
*/
public function __construct($ipAddress, $portNumber) {
if (!isset(self::$log)) {
// self::$log = new \Monolog\Logger('SourceSocket');
}
$this->buffer = ByteBuffer::allocate(1400);
$this->ipAddress = $ipAddress;
$this->portNumber = $portNumber;
}
/**
* Closes the underlying TCP socket if it exists
*
* @see SteamSocket::close()
*/
public function close() {
if(!empty($this->socket)) {
parent::close();
}
}
/**
* Sends the given RCON packet to the server
*
* @param SteamPacket $dataPacket The RCON packet to send to the server
*/
public function send(SteamPacket $dataPacket) {
if(empty($this->socket) || !$this->socket->isOpen()) {
$this->socket = new TCPSocket();
$this->socket->connect($this->ipAddress, $this->portNumber, SteamSocket::$timeout);
}
parent::send($dataPacket);
}
/**
* Reads a packet from the socket
*
* The Source RCON protocol allows packets of an arbitrary sice transmitted
* using multiple TCP packets. The data is received in chunks and
* concatenated into a single response packet.
*
* @return SteamPacket The packet replied from the server
* @throws RCONBanException if the IP of the local machine has been banned
* on the game server
* @throws RCONNoAuthException if an authenticated connection has been
* dropped by the server
*/
public function getReply() {
if ($this->receivePacket(4) == 0) {
$this->socket->close();
throw new RCONBanException();
}
$packetSize = $this->buffer->getLong();
$remainingBytes = $packetSize;
$packetData = '';
do {
$receivedBytes = $this->receivePacket($remainingBytes);
$remainingBytes -= $receivedBytes;
$packetData .= $this->buffer->get();
} while($remainingBytes > 0);
$packet = RCONPacketFactory::getPacketFromData($packetData);
//self::$log->addDebug('Received packet of type ' . get_class($packet));
return $packet;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2013, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'steam/packets/SteamPacketFactory.php';
require_once STEAM_CONDENSER_PATH . 'steam/sockets/SteamSocket.php';
/**
* This class represents a socket used to communicate with game servers based
* on the Source engine (e.g. Team Fortress 2, Counter-Strike: Source)
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage sockets
*/
class SourceSocket extends SteamSocket {
/**
* @var Monolog\Logger The Monolog logger for this class
*/
private static $log;
/**
* Creates a new UDP socket to communicate with the Source server on the
* given IP address and port
*
* @param string $ipAddress Either the IP address or the DNS name of the
* server
* @param int $portNumber The port the server is listening on
*/
public function __construct($ipAddress, $portNumber = 27015) {
parent::__construct($ipAddress, $portNumber);
if (!isset(self::$log)) {
//self::$log = new \Monolog\Logger('SourceSocket');
}
}
/**
* Reads a packet from the socket
*
* The Source query protocol specifies a maximum packet size of 1,400
* bytes. Bigger packets will be split over several UDP packets. This
* method reassembles split packets into single packet objects.
* Additionally Source may compress big packets using bzip2. Those packets
* will be compressed.
*
* @return SteamPacket The packet replied from the server
*/
public function getReply() {
$this->receivePacket(1400);
$isCompressed = false;
if($this->buffer->getLong() == -2) {
do {
$requestId = $this->buffer->getLong();
$isCompressed = (($requestId & 0x80000000) != 0);
$packetCount = $this->buffer->getByte();
$packetNumber = $this->buffer->getByte() + 1;
if($isCompressed) {
$splitSize = $this->buffer->getLong();
$packetChecksum = $this->buffer->getUnsignedLong();
} else {
$splitSize = $this->buffer->getShort();
}
$splitPackets[$packetNumber] = $this->buffer->get();
self::$log->addDebug("Received packet $packetNumber of $packetCount for request #$requestId");
if(sizeof($splitPackets) < $packetCount) {
try {
$bytesRead = $this->receivePacket();
} catch(TimeoutException $e) {
$bytesRead = 0;
}
} else {
$bytesRead = 0;
}
} while($bytesRead > 0 && $this->buffer->getLong() == -2);
if($isCompressed) {
$packet = SteamPacketFactory::reassemblePacket($splitPackets, true, $packetChecksum);
} else {
$packet = SteamPacketFactory::reassemblePacket($splitPackets);
}
} else {
$packet = SteamPacketFactory::getPacketFromData($this->buffer->get());
}
if($isCompressed) {
// self::$log->addDebug("Received compressed reply of type \"" . get_class($packet) . "\"");
} else {
// self::$log->addDebug("Received reply of type \"" . get_class($packet) . "\"");
}
return $packet;
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2013, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'ByteBuffer.php';
require_once STEAM_CONDENSER_PATH . 'UDPSocket.php';
require_once STEAM_CONDENSER_PATH . 'exceptions/SocketException.php';
require_once STEAM_CONDENSER_PATH . 'exceptions/TimeoutException.php';
require_once STEAM_CONDENSER_PATH . 'steam/packets/SteamPacketFactory.php';
/**
* This abstract class implements common functionality for sockets used to
* connect to game and master servers
*
* @author Sebastian Staudt
* @package steam-condenser
* @subpackage sockets
*/
abstract class SteamSocket {
/**
* @var Monolog\Logger The Monolog logger for this class
*/
private static $log;
/**
* @var int The default socket timeout
*/
protected static $timeout = 1000;
/**
* @var ByteBuffer
*/
protected $buffer;
/**
* @var UDPSocket
*/
protected $socket;
/**
* Sets the timeout for socket operations
*
* Any request that takes longer than this time will cause a {@link
* TimeoutException}.
*
* @param int $timeout The amount of milliseconds before a request times
* out
*/
public static function setTimeout($timeout) {
self::$timeout = $timeout;
}
/**
* Creates a new UDP socket to communicate with the server on the given IP
* address and port
*
* @param string $ipAddress Either the IP address or the DNS name of the
* server
* @param int $portNumber The port the server is listening on
*/
public function __construct($ipAddress, $portNumber = 27015) {
if (!isset(self::$log)) {
// self::$log = new \Monolog\Logger('SteamSocket');
}
$this->socket = new UDPSocket();
$this->socket->connect($ipAddress, $portNumber, 0);
}
/**
* Closes this socket
*
* @see #close()
*/
public function __destruct() {
$this->close();
}
/**
* Closes the underlying socket
*
* @see UDPSocket::close()
*/
public function close() {
if(!empty($this->socket) && $this->socket->isOpen()) {
$this->socket->close();
}
}
/**
* Subclasses have to implement this method for their individual packet
* formats
*
* @return SteamPacket The packet replied from the server
*/
abstract public function getReply();
/**
* Reads the given amount of data from the socket and wraps it into the
* buffer
*
* @param int $bufferLength The data length to read from the socket
* @throws SocketException if an error occurs while reading data
* @throws TimeoutException if no packet is received on time
* @return int The number of bytes that have been read from the socket
* @see ByteBuffer
*/
public function receivePacket($bufferLength = 0) {
if(!$this->socket->select(self::$timeout)) {
throw new TimeoutException();
}
if($bufferLength == 0) {
$this->buffer->clear();
} else {
$this->buffer = ByteBuffer::allocate($bufferLength);
}
try {
$data = $this->socket->recv($this->buffer->remaining());
} catch (SocketException $e) {
if (defined('SOCKET_ECONNRESET') &&
$e->getCode() == SOCKET_ECONNRESET) {
$this->socket->close();
}
throw $e;
}
$this->buffer->put($data);
$bytesRead = $this->buffer->position();
$this->buffer->rewind();
$this->buffer->limit($bytesRead);
return $bytesRead;
}
/**
* Sends the given packet to the server
*
* This converts the packet into a byte stream first before writing it to
* the socket.
*
* @param SteamPacket $dataPacket The packet to send to the server
* @see SteamPacket::__toString()
*/
public function send(SteamPacket $dataPacket) {
//self::$log->addDebug("Sending packet of type \"" . get_class($dataPacket) . "\"...");
$this->socket->send($dataPacket->__toString());
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2011, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'exceptions/SteamCondenserException.php';
/**
* This class represents a player connected to a game server
*
* @author Sebastian Staudt
* @package steam-condenser
*/
class SteamPlayer {
/**
* @var int
*/
private $clientPort;
/**
* @var float
*/
private $connectTime;
/**
* @var bool
*/
private $extended;
/**
* @var int
*/
private $id;
/**
* @var string
*/
private $ipAddress;
/**
* @var int
*/
private $loss;
/**
* @var string
*/
private $name;
/**
* @var int
*/
private $ping;
/**
* @var int
*/
private $rate;
/**
* @var int
*/
private $realId;
/**
* @var int
*/
private $score;
/**
* @var string
*/
private $state;
/**
* @var string
*/
private $steamId;
/**
* Creates a new player instancewith the given information
*
* @param int $id The ID of the player on the server
* @param string $name The name of the player
* @param int $score The score of the player
* @param float $connectTime The time the player is connected to the
* server
*/
public function __construct($id, $name, $score, $connectTime) {
$this->connectTime = $connectTime;
$this->id = $id;
$this->name = $name;
$this->score = $score;
$this->extended = false;
}
/**
* Extends a player object with information retrieved from a RCON call to
* the status command
*
* @param string $playerData The player data retrieved from
* <var>rcon status</var>
* @throws SteamCondenserException if the information belongs to another
* player
*/
public function addInformation($playerData) {
if($playerData['name'] != $this->name) {
throw new SteamCondenserException('Information to add belongs to a different player.');
}
$this->extended = true;
$this->realId = intval($playerData['userid']);
if(array_key_exists('state', $playerData)) {
$this->state = $playerData['state'];
}
$this->steamId = $playerData['uniqueid'];
if(!$this->isBot()) {
$this->loss = intval($playerData['loss']);
$this->ping = intval($playerData['ping']);
if(array_key_exists('adr', $playerData)) {
$address = explode(':', $playerData['adr']);
$this->ipAddress = $address[0];
$this->clientPort = intval($address[1]);
}
if(array_key_exists('rate', $playerData)) {
$this->rate = $playerData['rate'];
}
}
}
/**
* Returns the client port of this player
*
* @return int The client port of the player
*/
public function getClientPort() {
return $this->clientPort;
}
/**
* Returns the time this player is connected to the server
*
* @return float The connection time of the player
*/
public function getConnectTime() {
return $this->connectTime;
}
/**
* Returns the ID of this player
*
* @return int The ID of this player
*/
public function getId() {
return $this->id;
}
/**
* Returns the IP address of this player
*
* @return string The IP address of this player
*/
public function getIpAddress() {
return $this->ipAddress;
}
/**
* Returns the packet loss of this player's connection
*
* @return string The packet loss of this player's connection
*/
public function getLoss() {
return $this->loss;
}
/**
* Returns the nickname of this player
*
* @return string The name of this player
*/
public function getName() {
return $this->name;
}
/**
* Returns the ping of this player
*
* @return int The ping of this player
*/
public function getPing() {
return $this->ping;
}
/**
* Returns the rate of this player
*
* @return int The rate of this player
*/
public function getRate() {
return $this->rate;
}
/**
* Returns the real ID (as used on the server) of this player
*
* @return int The real ID of this player
*/
public function getRealId() {
return $this->realId;
}
/**
* Returns the score of this player
*
* @return int The score of this player
*/
public function getScore() {
return $this->score;
}
/**
* Returns the connection state of this player
*
* @return string The connection state of this player
*/
public function getState() {
return $this->state;
}
/**
* Returns the SteamID of this player
*
* @return string The SteamID of this player
*/
public function getSteamId() {
return $this->steamId;
}
/**
* Returns whether this player is a bot
*
* @return bool <var>true</var> if this player is a bot
*/
public function isBot() {
return $this->steamId == 'BOT';
}
/**
* Returns whether this player object has extended information gathered
* using RCON
*
* @return bool <var>true</var> if extended information for this player
* is available
*/
public function isExtended() {
return $this->extended;
}
/**
* Returns a string representation of this player
*
* @return string A string representing this player
*/
public function __toString() {
if($this->extended) {
return "#{$this->realId} \"{$this->name}\", SteamID: {$this->steamId} Score: {$this->score}, Time: {$this->connectTime}";
} else {
return "#{$this->id} \"{$this->name}\", Score: {$this->score}, Time: {$this->connectTime}";
}
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2013, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'Socket.php';
require_once STEAM_CONDENSER_PATH . 'exceptions/SteamCondenserException.php';
/**
* This class represents a TCP socket
*
* It can connect to a remote host, send and receive packets
*
* @author Sebastian Staudt
* @package steam-condenser
*/
class TCPSocket extends Socket {
/**
* Connects the TCP socket to the host with the given IP address and port
* number
*
* Depending on whether PHP's sockets extension is loaded, this uses either
* <var>socket_create</var>/<var>socket_connect</var> or
* <var>fsockopen</var>.
*
* @param string $ipAddress The IP address to connect to
* @param int $portNumber The TCP port to connect to
* @param int $timeout The timeout in milliseconds
* @throws SocketException if an error occurs during connecting the socket
*/
public function connect($ipAddress, $portNumber, $timeout) {
$this->ipAddress = $ipAddress;
$this->portNumber = $portNumber;
if($this->socketsEnabled) {
if (!$this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) {
throw new SocketException(socket_last_error($this->socket));
}
socket_set_nonblock($this->socket);
@socket_connect($this->socket, $ipAddress, $portNumber);
$write = array($this->socket);
$read = $except = array();
$sec = floor($timeout / 1000);
$usec = $timeout % 1000;
if(!socket_select($read, $write, $except, $sec, $usec)) {
$errorCode = socket_last_error($this->socket);
} else {
$errorCode = socket_get_option($this->socket, SOL_SOCKET, SO_ERROR);
}
if ($errorCode) {
throw new SocketException($errorCode);
}
socket_set_block($this->socket);
} else {
if (!$this->socket = @fsockopen("tcp://$ipAddress", $portNumber, $socketErrno, $socketErrstr, $timeout / 1000)) {
throw new SocketException($socketErrstr);
}
stream_set_blocking($this->socket, true);
}
}
}
<?php
/**
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2008-2013, Sebastian Staudt
*
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
*/
require_once STEAM_CONDENSER_PATH . 'Socket.php';
require_once STEAM_CONDENSER_PATH . 'exceptions/SteamCondenserException.php';
/**
* This class represents a UDP socket
*
* It can connect to a remote host, send and receive packets
*
* @author Sebastian Staudt
* @package steam-condenser
*/
class UDPSocket extends Socket {
/**
* Connects the UDP socket to the host with the given IP address and port
* number
*
* Depending on whether PHP's sockets extension is loaded, this uses either
* <var>socket_create</var>/<var>socket_connect</var> or
* <var>fsockopen</var>.
*
* @param string $ipAddress The IP address to connect to
* @param int $portNumber The UDP port to connect to
* @param int $timeout The timeout in milliseconds
* @throws SocketException if an error occurs during connecting the socket
*/
public function connect($ipAddress, $portNumber, $timeout) {
$this->ipAddress = $ipAddress;
$this->portNumber = $portNumber;
if($this->socketsEnabled) {
if(!$this->socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP)) {
throw new SocketException(socket_last_error($this->socket));
}
if(@!socket_connect($this->socket, $ipAddress, $portNumber)) {
throw new SocketException(socket_last_error($this->socket));
}
socket_set_block($this->socket);
} else {
if(!$this->socket = fsockopen("udp://$ipAddress", $portNumber, $socketErrno, $socketErrstr)) {
throw new SocketException($socketErrstr);
}
stream_set_blocking($this->socket, true);
}
}
}
<?php
/**
* User: gannon
* Date: 10/10/13
* Time: 1:00 PM
* SDRCServerInfoWidget is the controller for the SDRC Server Info Widget for wordpress
*/
//add stem condinser
include( plugin_dir_path( __FILE__ ) . 'lib/steam-condenser.php');
class SDRCServerInfoWidget extends WP_Widget {
public function __construct() {
$widget_ops = array('classname' => 'SDRCServerInfoWidget', 'description' => "Displays Server Info");
parent::WP_Widget(false, $name = 'SDRC Server Info Widget', $widget_ops);
}
/**
* Draw THe widget
* @param array $args
* @param array $instance
*/
public function widget( $args, $instance ) {
extract( $args );
$ip = $instance['ip'];
$port = $instance['port'];
$rcon = $instance['rcon'];
//set up the data for the view
$server = new SourceServer($ip,$port);
$server_info =$server->getServerInfo();
$player_list = $server->getPlayers($rcon);
//load tmplate file
echo $before_widget;
include( plugin_dir_path( __FILE__ ) . 'view/widget-main.tpl');
echo $after_widget;
}
public function form( $instance ) {
/* Set up some default widget settings. */
$defaults = array( 'ip' => __('19.168.1.1', 'example'), 'port' => __('27015', 'example'),'rcon' => __('1234', 'example'), );
$instance = wp_parse_args( (array) $instance, $defaults ); ?>
<!-- Server IP: Text Input -->
<p>
<label for="<?php echo $this->get_field_id( 'ip' ); ?>"><?php _e('Server IP:', 'hybrid'); ?></label>
<input id="<?php echo $this->get_field_id( 'ip' ); ?>" name="<?php echo $this->get_field_name( 'ip' ); ?>" value="<?php echo $instance['ip']; ?>" style="width:100%;" />
</p>
<!-- Server Port: Text Input -->
<p>
<label for="<?php echo $this->get_field_id( 'port' ); ?>"><?php _e('Server Port:', 'example'); ?></label>
<input id="<?php echo $this->get_field_id( 'port' ); ?>" name="<?php echo $this->get_field_name( 'port' ); ?>" value="<?php echo $instance['port']; ?>" style="width:100%;" />
</p>
<!-- Your Name: Text Input -->
<p>
<label for="<?php echo $this->get_field_id( 'rcon' ); ?>"><?php _e('RCON Password:', 'example'); ?></label>
<input id="<?php echo $this->get_field_id( 'rcon' ); ?>" name="<?php echo $this->get_field_name( 'rcon' ); ?>" value="<?php echo $instance['rcon']; ?>" style="width:100%;" />
</p>
<?php
// include( plugin_dir_path( __FILE__ ) . 'lib/admin-menu.tpl');
}
public function update( $new_instance, $old_instance ) {
$instance = $old_instance;
/* Strip tags for title and name to remove HTML (important for text inputs). */
$instance['ip'] = strip_tags( $new_instance['ip'] );
$instance['port'] = strip_tags( $new_instance['port'] );
$instance['rcon'] = strip_tags( $new_instance['rcon'] );
return $instance;
}
}
<?php
/**
* Plugin Name: SDRC Server Info
* Plugin URI: https://github.com/DavidGannon/SRCD-Server-Info
* Description: A WordPress Plunging to Display SRCD Server info
* Version: test
* Author: David Gannon
* Author URI: http://davidgannon.ca
* License: GPL3
*/
/**
* Set up Hooks
*/
//add an admin menu hook
add_action('admin_menu','srdc_server_info_admin_action');
//add widget hook
add_action( 'widgets_init', 'SDRCServerInfoWidget_register' );
/**
* Main admin set up
*/
function srdc_server_info_admin_action()
{
add_options_page('SDRC Server Info','SDRC Server Info','manage_options',__FILE__,'plugin_admin');
}
/**
* Admin Pae controller
*/
function plugin_admin(){
//load a view
include( plugin_dir_path( __FILE__ ) . 'admin-menu.tpl');
}
/**
* Register widget
*/
function SDRCServerInfoWidget_register() {
include( plugin_dir_path( __FILE__ ) . 'SDRCServerInfoWidget.php');
register_widget('SDRCServerInfoWidget');
}
?>
<h3 class="widget-title"><?php echo $server_info['serverName'] ; ?></h3>
Map: <?php echo $server_info['mapName']; ?> <br />
<?php echo $server_info['numberOfPlayers'];?>/<?php echo $server_info['maxPlayers']; ?>
<table>
<thead>
<tr>
<td>Name</td>
<td>Score</td>
<td>Time</td>
</tr>
</thead>
<tbody>
<?php foreach($player_list as $player):?>
<tr>
<td><?php echo $player->getName() ?></td>
<td><?php echo $player->getScore() ?></td>
<td><?php echo (round($player->getConnectTime()/60))." Min" ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment