1217 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			1217 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| 
 | |
| namespace iControl;
 | |
| 
 | |
| // TODO: show mix of best and next records (depending on own one)
 | |
| // TODO: enable keep-alive
 | |
| // TODO: enable gzip compression
 | |
| // TODO: log errors from WarningsAndTTR2
 | |
| // TODO: check map requirements (cp count + length)
 | |
| // TODO: check checkpoints on finish
 | |
| // TODO: threaded requests
 | |
| 
 | |
| /**
 | |
|  * iControl Records Plugin
 | |
|  *
 | |
|  * @author steeffeen
 | |
|  */
 | |
| class Plugin_Records {
 | |
| 	/**
 | |
| 	 * Constants
 | |
| 	 */
 | |
| 	const VERSION = '1.0';
 | |
| 	const MLID_LOCAL = 'ml_local_records';
 | |
| 	const MLID_DEDI = 'ml_dedimania_records';
 | |
| 	const TABLE_RECORDS = 'ic_records';
 | |
| 	const XMLRPC_MULTICALL = 'system.multicall';
 | |
| 	const DEDIMANIA_URL = 'http://dedimania.net:8081/Dedimania';
 | |
| 	const DEDIMANIA_OPENSESSION = 'dedimania.OpenSession';
 | |
| 	const DEDIMANIA_CHECKSESSION = 'dedimania.CheckSession';
 | |
| 	const DEDIMANIA_GETRECORDS = 'dedimania.GetChallengeRecords';
 | |
| 	const DEDIMANIA_PLAYERCONNECT = 'dedimania.PlayerConnect';
 | |
| 	const DEDIMANIA_PLAYERDISCONNECT = 'dedimania.PlayerDisconnect';
 | |
| 	const DEDIMANIA_UPDATESERVERPLAYERS = 'dedimania.UpdateServerPlayers';
 | |
| 	const DEDIMANIA_SETCHALLENGETIMES = 'dedimania.SetChallengeTimes';
 | |
| 	const DEDIMANIA_WARNINGSANDTTR2 = 'dedimania.WarningsAndTTR2';
 | |
| 
 | |
| 	/**
 | |
| 	 * Private properties
 | |
| 	 */
 | |
| 	private $iControl = null;
 | |
| 
 | |
| 	private $settings = null;
 | |
| 
 | |
| 	private $config = null;
 | |
| 
 | |
| 	private $mapInfo = null;
 | |
| 
 | |
| 	private $manialinks = array();
 | |
| 
 | |
| 	private $lastSendManialinks = array();
 | |
| 
 | |
| 	private $updateManialinks = array();
 | |
| 
 | |
| 	private $dedimaniaData = array();
 | |
| 
 | |
| 	private $checkpoints = array();
 | |
| 
 | |
| 	/**
 | |
| 	 * Constuct plugin
 | |
| 	 */
 | |
| 	public function __construct($iControl) {
 | |
| 		$this->iControl = $iControl;
 | |
| 		
 | |
| 		// Load config
 | |
| 		$this->config = Tools::loadConfig('records.plugin.xml');
 | |
| 		
 | |
| 		// Check for enabled setting
 | |
| 		if (!Tools::toBool($this->config->enabled)) return;
 | |
| 		
 | |
| 		// Load settings
 | |
| 		$this->loadSettings();
 | |
| 		
 | |
| 		// Init tables
 | |
| 		$this->initTables();
 | |
| 		
 | |
| 		// Register for callbacks
 | |
| 		$this->iControl->callbacks->registerCallbackHandler(Callbacks::CB_IC_ONINIT, $this, 'handleOnInit');
 | |
| 		$this->iControl->callbacks->registerCallbackHandler(Callbacks::CB_IC_1_SECOND, $this, 'handle1Second');
 | |
| 		$this->iControl->callbacks->registerCallbackHandler(Callbacks::CB_IC_1_MINUTE, $this, 'handle1Minute');
 | |
| 		$this->iControl->callbacks->registerCallbackHandler(Callbacks::CB_IC_3_MINUTE, $this, 'handle3Minute');
 | |
| 		$this->iControl->callbacks->registerCallbackHandler(Callbacks::CB_IC_BEGINMAP, $this, 'handleMapBegin');
 | |
| 		$this->iControl->callbacks->registerCallbackHandler(Callbacks::CB_IC_CLIENTUPDATED, $this, 'handleClientUpdated');
 | |
| 		$this->iControl->callbacks->registerCallbackHandler(Callbacks::CB_IC_ENDMAP, $this, 'handleMapEnd');
 | |
| 		$this->iControl->callbacks->registerCallbackHandler(Callbacks::CB_MP_PLAYERCONNECT, $this, 'handlePlayerConnect');
 | |
| 		$this->iControl->callbacks->registerCallbackHandler(Callbacks::CB_MP_PLAYERDISCONNECT, $this, 'handlePlayerDisconnect');
 | |
| 		$this->iControl->callbacks->registerCallbackHandler(Callbacks::CB_TM_PLAYERFINISH, $this, 'handlePlayerFinish');
 | |
| 		$this->iControl->callbacks->registerCallbackHandler(Callbacks::CB_TM_PLAYERCHECKPOINT, $this, 'handlePlayerCheckpoint');
 | |
| 		
 | |
| 		error_log('Records Pugin v' . self::VERSION . ' ready!');
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Init needed database tables
 | |
| 	 */
 | |
| 	private function initTables() {
 | |
| 		$database = $this->iControl->database;
 | |
| 		
 | |
| 		// Records table
 | |
| 		$query = "CREATE TABLE IF NOT EXISTS `" . self::TABLE_RECORDS . "` (
 | |
| 			`index` int(11) NOT NULL AUTO_INCREMENT,
 | |
| 			`mapUId` varchar(100) NOT NULL,
 | |
| 			`Login` varchar(100) NOT NULL,
 | |
| 			`time` int(11) NOT NULL,
 | |
| 			`changed` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
 | |
| 			PRIMARY KEY (`index`),
 | |
| 			UNIQUE KEY `player_map_record` (`mapUId`,`Login`)
 | |
| 			) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;";
 | |
| 		if (!$database->query($query)) {
 | |
| 			trigger_error("Couldn't create records table. " . $database->mysqli->error);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Load settings from config
 | |
| 	 */
 | |
| 	private function loadSettings() {
 | |
| 		$this->settings = new \stdClass();
 | |
| 		
 | |
| 		$this->settings->enabled = Tools::toBool($this->config->enabled);
 | |
| 		
 | |
| 		$this->settings->local_records_enabled = $this->settings->enabled && Tools::toBool($this->config->local_records->enabled);
 | |
| 		$this->settings->dedimania_enabled = $this->settings->enabled && Tools::toBool($this->config->dedimania_records->enabled);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Handle iControl init
 | |
| 	 */
 | |
| 	public function handleOnInit($callback = null) {
 | |
| 		// Let manialinks update
 | |
| 		if ($this->settings->local_records_enabled) {
 | |
| 			$this->updateManialinks[self::MLID_LOCAL] = true;
 | |
| 		}
 | |
| 		
 | |
| 		// Update mapinfo
 | |
| 		$this->mapInfo = $this->getMapInfo();
 | |
| 		
 | |
| 		if ($this->settings->dedimania_enabled) {
 | |
| 			// Open dedimania session
 | |
| 			$accounts = $this->config->xpath('dedimania_records/account');
 | |
| 			if (!$accounts) {
 | |
| 				trigger_error('Invalid dedimania_code in config.');
 | |
| 				$this->settings->dedimania_enabled = false;
 | |
| 			}
 | |
| 			else {
 | |
| 				$this->openDedimaniaSession();
 | |
| 			}
 | |
| 			$this->fetchDedimaniaRecords();
 | |
| 			$this->updateManialinks[self::MLID_DEDI] = true;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Fetch dedimania records of the current map
 | |
| 	 */
 | |
| 	private function fetchDedimaniaRecords($reset = true) {
 | |
| 		if (!isset($this->dedimaniaData['context'])) return false;
 | |
| 		if ($reset || !isset($this->dedimaniaData['records']) && !is_array($this->dedimaniaData['records'])) {
 | |
| 			// Reset records
 | |
| 			$this->dedimaniaData['records'] = array();
 | |
| 		}
 | |
| 		
 | |
| 		// Fetch records
 | |
| 		$servInfo = $this->getSrvInfo();
 | |
| 		$playerInfo = $this->getPlayerList();
 | |
| 		$gameMode = $this->getGameModeString();
 | |
| 		$data = array($this->dedimaniaData['sessionId'], $this->mapInfo, $gameMode, $servInfo, $playerInfo);
 | |
| 		$request = $this->encode_request(self::DEDIMANIA_GETRECORDS, $data);
 | |
| 		stream_context_set_option($this->dedimaniaData['context'], 'http', 'content', $request);
 | |
| 		$file = file_get_contents(self::DEDIMANIA_URL, false, $this->dedimaniaData['context']);
 | |
| 		
 | |
| 		// Handle response
 | |
| 		$response = $this->decode($file);
 | |
| 		if (is_array($response)) {
 | |
| 			foreach ($response as $index => $methodResponse) {
 | |
| 				if (xmlrpc_is_fault($methodResponse)) {
 | |
| 					$this->handleXmlRpcFault($methodResponse);
 | |
| 					return false;
 | |
| 				}
 | |
| 				else if ($index <= 0) {
 | |
| 					$responseData = $methodResponse[0];
 | |
| 					$this->dedimaniaData['records'] = $responseData;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		return true;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Checks dedimania session
 | |
| 	 */
 | |
| 	private function checkDedimaniaSession() {
 | |
| 		if (!$this->settings->dedimania_enabled) return false;
 | |
| 		if (!isset($this->dedimaniaData['context'])) return false;
 | |
| 		if (!isset($this->dedimaniaData['sessionId']) || !is_string($this->dedimaniaData['sessionId'])) return false;
 | |
| 		
 | |
| 		// Check session
 | |
| 		$request = $this->encode_request(self::DEDIMANIA_CHECKSESSION, array($this->dedimaniaData['sessionId']));
 | |
| 		stream_context_set_option($this->dedimaniaData['context'], 'http', 'content', $request);
 | |
| 		$file = file_get_contents(self::DEDIMANIA_URL, false, $this->dedimaniaData['context']);
 | |
| 		
 | |
| 		// Handle response
 | |
| 		$response = $this->decode($file);
 | |
| 		$result = false;
 | |
| 		if (is_array($response)) {
 | |
| 			foreach ($response as $methodResponse) {
 | |
| 				if (xmlrpc_is_fault($methodResponse)) {
 | |
| 					$this->handleXmlRpcFault($methodResponse);
 | |
| 				}
 | |
| 				else {
 | |
| 					$responseData = $methodResponse[0];
 | |
| 					if (is_bool($responseData)) {
 | |
| 						$result = $responseData;
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		return $result;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Renews dedimania session
 | |
| 	 */
 | |
| 	private function openDedimaniaSession($init = false) {
 | |
| 		if (!$this->settings->dedimania_enabled) return false;
 | |
| 		
 | |
| 		// Get server data
 | |
| 		if ($init || !array_key_exists('serverData', $this->dedimaniaData) || !is_array($this->dedimaniaData['serverData'])) {
 | |
| 			$serverData = array();
 | |
| 			$serverData['Game'] = 'TM2';
 | |
| 			$serverInfo = $this->iControl->server->getInfo(true);
 | |
| 			
 | |
| 			// Get dedimania account data
 | |
| 			$accounts = $this->config->xpath('dedimania_records/account');
 | |
| 			foreach ($accounts as $account) {
 | |
| 				$login = (string) $account->login;
 | |
| 				if ($login != $serverInfo['Login']) continue;
 | |
| 				$code = (string) $account->code;
 | |
| 				$serverData['Login'] = $login;
 | |
| 				$serverData['Code'] = $code;
 | |
| 				break;
 | |
| 			}
 | |
| 			
 | |
| 			if (!isset($serverData['Login']) || !isset($serverData['Code'])) {
 | |
| 				// Wrong configuration for current server
 | |
| 				trigger_error("Records Plugin: Invalid dedimania configuration for login '" . $serverInfo['Login'] . "'.");
 | |
| 				if (isset($this->dedimaniaData['context'])) unset($this->dedimaniaData['context']);
 | |
| 				if (isset($this->dedimaniaData['sessionId'])) unset($this->dedimaniaData['sessionId']);
 | |
| 				return false;
 | |
| 			}
 | |
| 			
 | |
| 			// Complete seesion data
 | |
| 			$serverData['Path'] = $serverInfo['Path'];
 | |
| 			$systemInfo = $this->iControl->server->getSystemInfo();
 | |
| 			$serverData['Packmask'] = substr($systemInfo['TitleId'], 2);
 | |
| 			$serverVersion = $this->iControl->server->getVersion();
 | |
| 			$serverData['ServerVersion'] = $serverVersion['Version'];
 | |
| 			$serverData['ServerBuild'] = $serverVersion['Build'];
 | |
| 			$serverData['Tool'] = 'iControl';
 | |
| 			$serverData['Version'] = iControl::VERSION;
 | |
| 			$this->dedimaniaData['serverData'] = $serverData;
 | |
| 		}
 | |
| 		
 | |
| 		// Init header
 | |
| 		if ($init || !array_key_exists('header', $this->dedimaniaData) || !is_array($this->dedimaniaData['header'])) {
 | |
| 			$header = '';
 | |
| 			$header .= 'Accept-Charset: utf-8;' . PHP_EOL;
 | |
| 			$header .= 'Accept-Encoding: gzip;' . PHP_EOL;
 | |
| 			$header .= 'Content-Type: text/xml; charset=utf-8;' . PHP_EOL;
 | |
| 			$header .= 'Keep-Alive: 300;' . PHP_EOL;
 | |
| 			$header .= 'User-Agent: iControl v' . iControl::VERSION . ';' . PHP_EOL;
 | |
| 			$this->dedimaniaData['header'] = $header;
 | |
| 		}
 | |
| 		
 | |
| 		// Open session
 | |
| 		$request = $this->encode_request(self::DEDIMANIA_OPENSESSION, array($this->dedimaniaData['serverData']));
 | |
| 		$context = stream_context_create(array('http' => array('method' => 'POST', 'header' => $this->dedimaniaData['header'])));
 | |
| 		stream_context_set_option($context, 'http', 'content', $request);
 | |
| 		$file = file_get_contents(self::DEDIMANIA_URL, false, $context);
 | |
| 		
 | |
| 		// Handle response
 | |
| 		$response = $this->decode($file);
 | |
| 		$result = false;
 | |
| 		if (is_array($response)) {
 | |
| 			foreach ($response as $index => $methodResponse) {
 | |
| 				if (xmlrpc_is_fault($methodResponse)) {
 | |
| 					$this->handleXmlRpcFault($methodResponse);
 | |
| 				}
 | |
| 				else if ($index <= 0) {
 | |
| 					$responseData = $methodResponse[0];
 | |
| 					$this->dedimaniaData['context'] = $context;
 | |
| 					$this->dedimaniaData['sessionId'] = $responseData['SessionId'];
 | |
| 					$result = true;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		if ($result) error_log("Dedimania connection successfully established.");
 | |
| 		return $result;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Handle 1Second callback
 | |
| 	 */
 | |
| 	public function handle1Second() {
 | |
| 		if (!$this->settings->enabled) return;
 | |
| 		
 | |
| 		// Send records manialinks if needed
 | |
| 		foreach ($this->updateManialinks as $id => $update) {
 | |
| 			if (!$update) continue;
 | |
| 			if (array_key_exists($id, $this->lastSendManialinks) && $this->lastSendManialinks[$id] + 2 > time()) continue;
 | |
| 			
 | |
| 			switch ($id) {
 | |
| 				case self::MLID_LOCAL:
 | |
| 					{
 | |
| 						$this->manialinks[$id] = $this->buildLocalManialink();
 | |
| 						break;
 | |
| 					}
 | |
| 				case self::MLID_DEDI:
 | |
| 					{
 | |
| 						$this->manialinks[$id] = $this->buildDedimaniaManialink();
 | |
| 						break;
 | |
| 					}
 | |
| 				default:
 | |
| 					{
 | |
| 						continue 2;
 | |
| 						break;
 | |
| 					}
 | |
| 			}
 | |
| 			$this->updateManialinks[$id] = false;
 | |
| 			$this->lastSendManialinks[$id] = time();
 | |
| 			$this->sendManialink($this->manialinks[$id]);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Handle 1Minute callback
 | |
| 	 */
 | |
| 	public function handle1Minute($callback = null) {
 | |
| 		if ($this->settings->dedimania_enabled) {
 | |
| 			// Keep dedimania session alive
 | |
| 			if (!$this->checkDedimaniaSession()) {
 | |
| 				// Renew session
 | |
| 				$this->openDedimaniaSession();
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Handle PlayerConnect callback
 | |
| 	 */
 | |
| 	public function handlePlayerConnect($callback) {
 | |
| 		$login = $callback[1][0];
 | |
| 		if ($this->settings->local_records_enabled) $this->sendManialink($this->manialinks[self::MLID_LOCAL], $login);
 | |
| 		
 | |
| 		if ($this->settings->dedimania_enabled && $this->dedimaniaData['context']) {
 | |
| 			$player = $this->iControl->server->getPlayer($login, true);
 | |
| 			if ($player) {
 | |
| 				// Send dedimania request
 | |
| 				$data = array($this->dedimaniaData['sessionId'], $player['Login'], $player['NickName'], $player['Path'], 
 | |
| 					$player['IsSpectator']);
 | |
| 				$request = $this->encode_request(self::DEDIMANIA_PLAYERCONNECT, $data);
 | |
| 				stream_context_set_option($this->dedimaniaData['context'], 'http', 'content', $request);
 | |
| 				$file = file_get_contents(self::DEDIMANIA_URL, false, $this->dedimaniaData['context']);
 | |
| 				
 | |
| 				// Handle response
 | |
| 				$response = $this->decode($file);
 | |
| 				if (is_array($response)) {
 | |
| 					foreach ($response as $methodResponse) {
 | |
| 						if (xmlrpc_is_fault($methodResponse)) {
 | |
| 							$this->handleXmlRpcFault($methodResponse);
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 				else {
 | |
| 					if (!$response) {
 | |
| 						trigger_error('XmlRpc Error.');
 | |
| 						var_dump($response);
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 			$this->sendManialink($this->manialinks[self::MLID_DEDI], $login);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Handle PlayerDisconnect callback
 | |
| 	 */
 | |
| 	public function handlePlayerDisconnect($callback) {
 | |
| 		$login = $callback[1][0];
 | |
| 		
 | |
| 		if ($this->settings->dedimania_enabled && $this->dedimaniaData['context']) {
 | |
| 			// Send dedimania request
 | |
| 			$data = array($this->dedimaniaData['sessionId'], $login, '');
 | |
| 			$request = $this->encode_request(self::DEDIMANIA_PLAYERDISCONNECT, $data);
 | |
| 			stream_context_set_option($this->dedimaniaData['context'], 'http', 'content', $request);
 | |
| 			$file = file_get_contents(self::DEDIMANIA_URL, false, $this->dedimaniaData['context']);
 | |
| 			
 | |
| 			// Handle response
 | |
| 			$response = $this->decode($file);
 | |
| 			if (is_array($response)) {
 | |
| 				foreach ($response as $methodResponse) {
 | |
| 					if (xmlrpc_is_fault($methodResponse)) {
 | |
| 						$this->handleXmlRpcFault($methodResponse);
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 			else {
 | |
| 				if (!$response) {
 | |
| 					trigger_error('XmlRpc Error.');
 | |
| 					var_dump($response);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Handle BeginMap callback
 | |
| 	 */
 | |
| 	public function handleMapBegin($callback) {
 | |
| 		// Update map
 | |
| 		$this->mapInfo = $this->getMapInfo();
 | |
| 		
 | |
| 		if ($this->settings->local_records_enabled) {
 | |
| 			// Update local records
 | |
| 			$this->updateManialinks[self::MLID_LOCAL] = true;
 | |
| 		}
 | |
| 		
 | |
| 		if ($this->settings->dedimania_enabled) {
 | |
| 			// Update dedimania records
 | |
| 			$this->fetchDedimaniaRecords(true);
 | |
| 			$this->updateManialinks[self::MLID_DEDI] = true;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Build map info struct for dedimania requests
 | |
| 	 */
 | |
| 	private function getMapInfo() {
 | |
| 		$map = $this->iControl->server->getMap();
 | |
| 		if (!$map) return null;
 | |
| 		$mapInfo = array();
 | |
| 		$mapInfo['UId'] = $map['UId'];
 | |
| 		$mapInfo['Name'] = $map['Name'];
 | |
| 		$mapInfo['Author'] = $map['Author'];
 | |
| 		$mapInfo['Environment'] = $map['Environnement'];
 | |
| 		$mapInfo['NbCheckpoints'] = $map['NbCheckpoints'];
 | |
| 		$mapInfo['NbLaps'] = $map['NbLaps'];
 | |
| 		return $mapInfo;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Handle EndMap callback
 | |
| 	 */
 | |
| 	public function handleMapEnd($callback) {
 | |
| 		if ($this->settings->dedimania_enabled) {
 | |
| 			// Send dedimania records
 | |
| 			$gameMode = $this->getGameModeString();
 | |
| 			$times = array();
 | |
| 			$replays = array();
 | |
| 			foreach ($this->dedimaniaData['records']['Records'] as $record) {
 | |
| 				if (!isset($record['New']) || !$record['New']) continue;
 | |
| 				array_push($times, array('Login' => $record['Login'], 'Best' => $record['Best'], 'Checks' => $record['Checks']));
 | |
| 				if (!isset($replays['VReplay'])) {
 | |
| 					$replays['VReplay'] = $record['VReplay'];
 | |
| 				}
 | |
| 				if (!isset($replays['Top1GReplay']) && isset($record['Top1GReplay'])) {
 | |
| 					$replays['Top1GReplay'] = $record['Top1GReplay'];
 | |
| 				}
 | |
| 				// TODO: VReplayChecks
 | |
| 			}
 | |
| 			if (!isset($replays['VReplay'])) $replays['VReplay'] = '';
 | |
| 			if (!isset($replays['VReplayChecks'])) $replays['VReplayChecks'] = '';
 | |
| 			if (!isset($replays['Top1GReplay'])) $replays['Top1GReplay'] = '';
 | |
| 			
 | |
| 			xmlrpc_set_type($replays['VReplay'], 'base64');
 | |
| 			xmlrpc_set_type($replays['Top1GReplay'], 'base64');
 | |
| 			
 | |
| 			$data = array($this->dedimaniaData['sessionId'], $this->mapInfo, $gameMode, $times, $replays);
 | |
| 			$request = $this->encode_request(self::DEDIMANIA_SETCHALLENGETIMES, $data);
 | |
| 			stream_context_set_option($this->dedimaniaData['context'], 'http', 'content', $request);
 | |
| 			$file = file_get_contents(self::DEDIMANIA_URL, false, $this->dedimaniaData['context']);
 | |
| 			
 | |
| 			// Handle response
 | |
| 			$response = $this->decode($file);
 | |
| 			if (is_array($response)) {
 | |
| 				foreach ($response as $index => $methodResponse) {
 | |
| 					if (xmlrpc_is_fault($methodResponse)) {
 | |
| 						$this->handleXmlRpcFault($methodResponse);
 | |
| 					}
 | |
| 					else {
 | |
| 						if ($index <= 0) {
 | |
| 							// Called method response
 | |
| 							$responseData = $methodResponse[0];
 | |
| 							if (!$responseData) {
 | |
| 								trigger_error("Records Plugin: Submitting dedimania records failed.");
 | |
| 							}
 | |
| 							continue;
 | |
| 						}
 | |
| 						
 | |
| 						// Warnings and TTR
 | |
| 						$errors = $methodResponse[0]['methods'][0]['errors'];
 | |
| 						if ($errors) {
 | |
| 							trigger_error($errors);
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Get current checkpoint string for dedimania record
 | |
| 	 *
 | |
| 	 * @param string $login        	
 | |
| 	 * @return string
 | |
| 	 */
 | |
| 	private function getChecks($login) {
 | |
| 		if (!$login || !isset($this->checkpoints[$login])) return null;
 | |
| 		$string = '';
 | |
| 		$count = count($this->checkpoints[$login]);
 | |
| 		foreach ($this->checkpoints[$login] as $index => $check) {
 | |
| 			$string .= $check;
 | |
| 			if ($index < $count - 1) $string .= ',';
 | |
| 		}
 | |
| 		return $string;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Build server info struct for callbacks
 | |
| 	 */
 | |
| 	private function getSrvInfo() {
 | |
| 		$server = $this->iControl->server->getOptions();
 | |
| 		if (!$server) return null;
 | |
| 		$client = null;
 | |
| 		$players = null;
 | |
| 		$spectators = null;
 | |
| 		$this->iControl->server->getPlayers($client, $players, $spectators);
 | |
| 		if (!is_array($players) || !is_array($spectators)) return null;
 | |
| 		return array('SrvName' => $server['Name'], 'Comment' => $server['Comment'], 'Private' => (strlen($server['Password']) > 0), 
 | |
| 			'NumPlayers' => count($players), 'MaxPlayers' => $server['CurrentMaxPlayers'], 'NumSpecs' => count($spectators), 
 | |
| 			'MaxSpecs' => $server['CurrentMaxSpectators']);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Build simple player list for callbacks
 | |
| 	 */
 | |
| 	private function getPlayerList($votes = false) {
 | |
| 		$client = null;
 | |
| 		$players;
 | |
| 		$spectators;
 | |
| 		$allPlayers = $this->iControl->server->getPlayers($client, $players, $spectators);
 | |
| 		if (!is_array($players) || !is_array($spectators)) return null;
 | |
| 		$playerInfo = array();
 | |
| 		foreach ($allPlayers as $player) {
 | |
| 			array_push($playerInfo, array('Login' => $player['Login'], 'IsSpec' => in_array($player, $spectators)));
 | |
| 		}
 | |
| 		return $playerInfo;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Get dedi string representation of the current game mode
 | |
| 	 */
 | |
| 	private function getGameModeString() {
 | |
| 		$gameMode = $this->iControl->server->getGameMode();
 | |
| 		if ($gameMode === null) {
 | |
| 			trigger_error("Couldn't retrieve game mode. " . $this->iControl->getClientErrorText());
 | |
| 			return null;
 | |
| 		}
 | |
| 		switch ($gameMode) {
 | |
| 			case 1:
 | |
| 			case 3:
 | |
| 			case 5:
 | |
| 				{
 | |
| 					return 'Rounds';
 | |
| 				}
 | |
| 			case 2:
 | |
| 			case 4:
 | |
| 				{
 | |
| 					return 'TA';
 | |
| 				}
 | |
| 		}
 | |
| 		return null;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Build votes info struct for callbacks
 | |
| 	 */
 | |
| 	private function getVotesInfo() {
 | |
| 		$map = $this->iControl->server->getMap();
 | |
| 		if (!$map) return null;
 | |
| 		$gameMode = $this->getGameModeString();
 | |
| 		if (!$gameMode) return null;
 | |
| 		return array('UId' => $map['UId'], 'GameMode' => $gameMode);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Handle 3Minute callback
 | |
| 	 */
 | |
| 	public function handle3Minute($callback = null) {
 | |
| 		if ($this->settings->dedimania_enabled) {
 | |
| 			// Update dedimania players
 | |
| 			$servInfo = $this->getSrvInfo();
 | |
| 			$votesInfo = $this->getVotesInfo();
 | |
| 			$playerList = $this->getPlayerList(true);
 | |
| 			if ($servInfo && $votesInfo && $playerList) {
 | |
| 				$data = array($this->dedimaniaData['sessionId'], $servInfo, $votesInfo, $playerList);
 | |
| 				$request = $this->encode_request(self::DEDIMANIA_UPDATESERVERPLAYERS, $data);
 | |
| 				stream_context_set_option($this->dedimaniaData['context'], 'http', 'content', $request);
 | |
| 				$file = file_get_contents(self::DEDIMANIA_URL, false, $this->dedimaniaData['context']);
 | |
| 				
 | |
| 				// Handle response
 | |
| 				$response = $this->decode($file);
 | |
| 				if (is_array($response)) {
 | |
| 					foreach ($response as $methodResponse) {
 | |
| 						if (xmlrpc_is_fault($methodResponse)) {
 | |
| 							$this->handleXmlRpcFault($methodResponse);
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 				else if (!$response) {
 | |
| 					trigger_error('XmlRpc Error.');
 | |
| 					var_dump($response);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Handle PlayerCheckpoint callback
 | |
| 	 */
 | |
| 	public function handlePlayerCheckpoint($callback) {
 | |
| 		$data = $callback[1];
 | |
| 		$login = $data[1];
 | |
| 		$time = $data[2];
 | |
| 		$lap = $data[3];
 | |
| 		$cpIndex = $data[4];
 | |
| 		if (!isset($this->checkpoints[$login]) || $cpIndex <= 0) $this->checkpoints[$login] = array();
 | |
| 		$this->checkpoints[$login][$cpIndex] = $time;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Handle PlayerFinish callback
 | |
| 	 */
 | |
| 	public function handlePlayerFinish($callback) {
 | |
| 		$data = $callback[1];
 | |
| 		if ($data[0] <= 0 || $data[2] <= 0) return;
 | |
| 		
 | |
| 		$login = $data[1];
 | |
| 		$time = $data[2];
 | |
| 		$newMap = $this->iControl->server->getMap();
 | |
| 		if (!$newMap) return;
 | |
| 		if (!$this->mapInfo || $this->mapInfo['UId'] !== $newMap['UId']) {
 | |
| 			$this->mapInfo = $this->getMapInfo();
 | |
| 		}
 | |
| 		$map = $newMap;
 | |
| 		
 | |
| 		$player = $this->iControl->server->getPlayer($login);
 | |
| 		
 | |
| 		if ($this->settings->local_records_enabled) {
 | |
| 			// Get old record of the player
 | |
| 			$oldRecord = $this->getLocalRecord($map['UId'], $login);
 | |
| 			$save = true;
 | |
| 			if ($oldRecord) {
 | |
| 				if ($oldRecord['time'] < $time) {
 | |
| 					// Not improved
 | |
| 					$save = false;
 | |
| 				}
 | |
| 				else if ($oldRecord['time'] == $time) {
 | |
| 					// Same time
 | |
| 					$message = '$<' . $player['NickName'] . '$> equalized her/his $<$o' . $oldRecord['rank'] . '.$> Local Record: ' .
 | |
| 							 Tools::formatTime($oldRecord['time']);
 | |
| 					$this->iControl->chat->sendInformation($message);
 | |
| 					$save = false;
 | |
| 				}
 | |
| 			}
 | |
| 			if ($save) {
 | |
| 				// Save time
 | |
| 				$database = $this->iControl->database;
 | |
| 				$query = "INSERT INTO `" . self::TABLE_RECORDS . "` (
 | |
| 					`mapUId`,
 | |
| 					`Login`,
 | |
| 					`time`
 | |
| 					) VALUES (
 | |
| 					'" . $database->escape($map['UId']) . "',
 | |
| 					'" . $database->escape($login) . "',
 | |
| 					" . $time . "
 | |
| 					) ON DUPLICATE KEY UPDATE
 | |
| 					`time` = VALUES(`time`);";
 | |
| 				if (!$database->query($query)) {
 | |
| 					trigger_error("Couldn't save player record. " . $database->mysqli->error);
 | |
| 				}
 | |
| 				else {
 | |
| 					// Announce record
 | |
| 					$newRecord = $this->getLocalRecord($map['UId'], $login);
 | |
| 					if ($oldRecord == null || $newRecord['rank'] < $oldRecord['rank']) {
 | |
| 						// Gained rank
 | |
| 						$improvement = 'gained the';
 | |
| 					}
 | |
| 					else {
 | |
| 						// Only improved time
 | |
| 						$improvement = 'improved her/his';
 | |
| 					}
 | |
| 					$message = '$<' . $player['NickName'] . '$> ' . $improvement . ' $<$o' . $newRecord['rank'] . '.$> Local Record: ' .
 | |
| 							 Tools::formatTime($newRecord['time']);
 | |
| 					$this->iControl->chat->sendInformation($message);
 | |
| 					$this->updateManialinks[self::MLID_LOCAL] = true;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		
 | |
| 		if ($this->settings->dedimania_enabled) {
 | |
| 			// Get old record of the player
 | |
| 			$oldRecord = $this->getDediRecord($login);
 | |
| 			$save = true;
 | |
| 			if ($oldRecord) {
 | |
| 				if ($oldRecord['Best'] < $time) {
 | |
| 					// Not improved
 | |
| 					$save = false;
 | |
| 				}
 | |
| 				else if ($oldRecord['Best'] == $time) {
 | |
| 					// Same time
 | |
| 					$save = false;
 | |
| 				}
 | |
| 			}
 | |
| 			if ($save) {
 | |
| 				// Save time
 | |
| 				$newRecord = array('Login' => $login, 'NickName' => $player['NickName'], 'Best' => $data[2], 
 | |
| 					'Checks' => $this->getChecks($login), 'New' => true);
 | |
| 				$inserted = $this->insertDediRecord($newRecord, $oldRecord);
 | |
| 				if ($inserted) {
 | |
| 					// Get newly saved record
 | |
| 					foreach ($this->dedimaniaData['records']['Records'] as $key => &$record) {
 | |
| 						if ($record['Login'] !== $newRecord['Login']) continue;
 | |
| 						$newRecord = $record;
 | |
| 						break;
 | |
| 					}
 | |
| 					
 | |
| 					// Announce record
 | |
| 					if (!$oldRecord || $newRecord['Rank'] < $oldRecord['Rank']) {
 | |
| 						// Gained rank
 | |
| 						$improvement = 'gained the';
 | |
| 					}
 | |
| 					else {
 | |
| 						// Only improved time
 | |
| 						$improvement = 'improved her/his';
 | |
| 					}
 | |
| 					$message = '$<' . $player['NickName'] . '$> ' . $improvement . ' $<$o' . $newRecord['Rank'] .
 | |
| 							 '.$> Dedimania Record: ' . Tools::formatTime($newRecord['Best']);
 | |
| 					$this->iControl->chat->sendInformation($message);
 | |
| 					$this->updateManialinks[self::MLID_DEDI] = true;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Get max rank for given login
 | |
| 	 */
 | |
| 	private function getMaxRank($login) {
 | |
| 		if (!isset($this->dedimaniaData['records'])) return null;
 | |
| 		$records = $this->dedimaniaData['records'];
 | |
| 		$maxRank = $records['ServerMaxRank'];
 | |
| 		foreach ($records['Players'] as $player) {
 | |
| 			if ($player['Login'] === $login) {
 | |
| 				if ($player['MaxRank'] > $maxRank) $maxRank = $player['MaxRank'];
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 		return $maxRank;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Inserts the given new dedimania record at the proper position
 | |
| 	 *
 | |
| 	 * @param struct $newRecord        	
 | |
| 	 * @return bool
 | |
| 	 */
 | |
| 	private function insertDediRecord(&$newRecord, $oldRecord) {
 | |
| 		if (!$newRecord || !isset($this->dedimaniaData['records']) || !isset($this->dedimaniaData['records']['Records'])) return false;
 | |
| 		
 | |
| 		$insert = false;
 | |
| 		$newRecords = array();
 | |
| 		
 | |
| 		// Get max possible rank
 | |
| 		$maxRank = $this->getMaxRank($newRecord['Login']);
 | |
| 		if (!$maxRank) $maxRank = 30;
 | |
| 		
 | |
| 		// Loop through existing records
 | |
| 		foreach ($this->dedimaniaData['records']['Records'] as $key => &$record) {
 | |
| 			if ($record['Rank'] > $maxRank) {
 | |
| 				// Max rank reached
 | |
| 				return false;
 | |
| 			}
 | |
| 			
 | |
| 			if ($record['Login'] === $newRecord['Login']) {
 | |
| 				// Old record of the same player
 | |
| 				if ($record['Best'] <= $newRecord['Best']) {
 | |
| 					// It's better - Do nothing
 | |
| 					return false;
 | |
| 				}
 | |
| 				
 | |
| 				// Replace old record
 | |
| 				unset($this->dedimaniaData['records']['Records'][$key]);
 | |
| 				$insert = true;
 | |
| 				break;
 | |
| 			}
 | |
| 			
 | |
| 			// Other player's record
 | |
| 			if ($record['Best'] <= $newRecord['Best']) {
 | |
| 				// It's better - Skip
 | |
| 				continue;
 | |
| 			}
 | |
| 			
 | |
| 			// New record is better - Insert it
 | |
| 			$insert = true;
 | |
| 			if ($oldRecord) {
 | |
| 				// Remove old record
 | |
| 				foreach ($this->dedimaniaData['records']['Records'] as $key2 => $record2) {
 | |
| 					if ($record2['Login'] !== $oldRecord['Login']) continue;
 | |
| 					unset($this->dedimaniaData['records']['Records'][$key2]);
 | |
| 					break;
 | |
| 				}
 | |
| 			}
 | |
| 			break;
 | |
| 		}
 | |
| 		
 | |
| 		if (!$insert && count($this->dedimaniaData['records']['Records']) < $maxRank) {
 | |
| 			// Records list not full - Append new record
 | |
| 			$insert = true;
 | |
| 		}
 | |
| 		
 | |
| 		if ($insert) {
 | |
| 			// Insert new record
 | |
| 			array_push($this->dedimaniaData['records']['Records'], $newRecord);
 | |
| 			
 | |
| 			// Update ranks
 | |
| 			$this->updateDediRecordRanks();
 | |
| 			
 | |
| 			// Save replays
 | |
| 			foreach ($this->dedimaniaData['records']['Records'] as $key => &$record) {
 | |
| 				if ($record['Login'] !== $newRecord['Login']) continue;
 | |
| 				$this->setRecordReplays($record);
 | |
| 				break;
 | |
| 			}
 | |
| 			
 | |
| 			// Record inserted
 | |
| 			return true;
 | |
| 		}
 | |
| 		
 | |
| 		// No new record
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Updates the replay values for the given record
 | |
| 	 *
 | |
| 	 * @param struct $record        	
 | |
| 	 */
 | |
| 	private function setRecordReplays(&$record) {
 | |
| 		if (!$record || !$this->settings->dedimania_enabled) return;
 | |
| 		
 | |
| 		// Set validation replay
 | |
| 		$validationReplay = $this->iControl->server->getValidationReplay($record['Login']);
 | |
| 		if ($validationReplay) $record['VReplay'] = $validationReplay;
 | |
| 		
 | |
| 		// Set ghost replay
 | |
| 		if ($record['Rank'] <= 1) {
 | |
| 			$dataDirectory = $this->iControl->server->getDataDirectory();
 | |
| 			if (!isset($this->dedimaniaData['directoryAccessChecked'])) {
 | |
| 				$access = $this->iControl->server->checkAccess($dataDirectory);
 | |
| 				if (!$access) {
 | |
| 					trigger_error("No access to the servers data directory. Can't retrieve ghost replays.");
 | |
| 				}
 | |
| 				$this->dedimaniaData['directoryAccessChecked'] = $access;
 | |
| 			}
 | |
| 			if ($this->dedimaniaData['directoryAccessChecked']) {
 | |
| 				$ghostReplay = $this->iControl->server->getGhostReplay($record['Login']);
 | |
| 				if ($ghostReplay) $record['Top1GReplay'] = $ghostReplay;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Update the sorting and the ranks of all dedimania records
 | |
| 	 */
 | |
| 	private function updateDediRecordRanks() {
 | |
| 		if (!isset($this->dedimaniaData['records']) || !isset($this->dedimaniaData['records']['Records'])) return;
 | |
| 		
 | |
| 		// Sort records
 | |
| 		usort($this->dedimaniaData['records']['Records'], array($this, 'compareRecords'));
 | |
| 		
 | |
| 		// Update ranks
 | |
| 		$rank = 1;
 | |
| 		foreach ($this->dedimaniaData['records']['Records'] as &$record) {
 | |
| 			$record['Rank'] = $rank;
 | |
| 			$rank++;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Compare function for sorting dedimania records
 | |
| 	 *
 | |
| 	 * @param struct $first        	
 | |
| 	 * @param struct $second        	
 | |
| 	 * @return int
 | |
| 	 */
 | |
| 	private function compareRecords($first, $second) {
 | |
| 		if ($first['Best'] < $second['Best']) {
 | |
| 			return -1;
 | |
| 		}
 | |
| 		else if ($first['Best'] > $second['Best']) {
 | |
| 			return 1;
 | |
| 		}
 | |
| 		else {
 | |
| 			if ($first['Rank'] < $second['Rank']) {
 | |
| 				return -1;
 | |
| 			}
 | |
| 			else {
 | |
| 				return 1;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Get the dedimania record of the given login
 | |
| 	 *
 | |
| 	 * @param string $login        	
 | |
| 	 * @return struct
 | |
| 	 */
 | |
| 	private function getDediRecord($login) {
 | |
| 		if (!isset($this->dedimaniaData['records'])) return null;
 | |
| 		$records = $this->dedimaniaData['records']['Records'];
 | |
| 		foreach ($records as $index => $record) {
 | |
| 			if ($record['Login'] === $login) return $record;
 | |
| 		}
 | |
| 		return null;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Send manialink to clients
 | |
| 	 */
 | |
| 	private function sendManialink($manialink, $login = null) {
 | |
| 		if (!$manialink || !$this->iControl->client) return;
 | |
| 		if (!$login) {
 | |
| 			if (!$this->iControl->client->query('SendDisplayManialinkPage', $manialink->asXML(), 0, false)) {
 | |
| 				trigger_error("Couldn't send manialink to players. " . $this->iControl->getClientErrorText());
 | |
| 			}
 | |
| 		}
 | |
| 		else {
 | |
| 			if (!$this->iControl->client->query('SendDisplayManialinkPageToLogin', $login, $manialink->asXML(), 0, false)) {
 | |
| 				trigger_error("Couldn't send manialink to player '" . $login . "'. " . $this->iControl->getClientErrorText());
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Handle ClientUpdated callback
 | |
| 	 *
 | |
| 	 * @param mixed $data        	
 | |
| 	 */
 | |
| 	public function handleClientUpdated($data) {
 | |
| 		$this->openDedimaniaSession(true);
 | |
| 		if (isset($this->updateManialinks[self::MLID_LOCAL])) $this->updateManialinks[self::MLID_LOCAL] = true;
 | |
| 		if (isset($this->updateManialinks[self::MLID_DEDI])) $this->updateManialinks[self::MLID_DEDI] = true;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Update local records manialink
 | |
| 	 */
 | |
| 	private function buildLocalManialink() {
 | |
| 		$map = $this->iControl->server->getMap();
 | |
| 		if (!$map) {
 | |
| 			return null;
 | |
| 		}
 | |
| 		
 | |
| 		$pos_x = (float) $this->config->local_records->widget->pos_x;
 | |
| 		$pos_y = (float) $this->config->local_records->widget->pos_y;
 | |
| 		$title = (string) $this->config->local_records->widget->title;
 | |
| 		$width = (float) $this->config->local_records->widget->width;
 | |
| 		$lines = (int) $this->config->local_records->widget->lines;
 | |
| 		$line_height = (float) $this->config->local_records->widget->line_height;
 | |
| 		
 | |
| 		$recordResult = $this->getLocalRecords($map['UId']);
 | |
| 		if (!$recordResult) {
 | |
| 			trigger_error("Couldn't fetch player records.");
 | |
| 			return null;
 | |
| 		}
 | |
| 		
 | |
| 		$xml = Tools::newManialinkXml(self::MLID_LOCAL);
 | |
| 		
 | |
| 		$frame = $xml->addChild('frame');
 | |
| 		$frame->addAttribute('posn', $pos_x . ' ' . $pos_y);
 | |
| 		
 | |
| 		// Background
 | |
| 		$quad = $frame->addChild('quad');
 | |
| 		Tools::addAlignment($quad, 'center', 'top');
 | |
| 		$quad->addAttribute('sizen', ($width * 1.05) . ' ' . (7. + $lines * $line_height));
 | |
| 		$quad->addAttribute('style', 'Bgs1InRace');
 | |
| 		$quad->addAttribute('substyle', 'BgTitleShadow');
 | |
| 		
 | |
| 		// Title
 | |
| 		$label = $frame->addChild('label');
 | |
| 		Tools::addAlignment($label);
 | |
| 		Tools::addTranslate($xml);
 | |
| 		$label->addAttribute('posn', '0 ' . ($line_height * -0.9));
 | |
| 		$label->addAttribute('sizen', $width . ' 0');
 | |
| 		$label->addAttribute('style', 'TextTitle1');
 | |
| 		$label->addAttribute('textsize', '2');
 | |
| 		$label->addAttribute('text', $title);
 | |
| 		
 | |
| 		// Times
 | |
| 		$index = 0;
 | |
| 		while ($record = $recordResult->fetch_assoc()) {
 | |
| 			$y = -8. - $index * $line_height;
 | |
| 			
 | |
| 			$recordFrame = $frame->addChild('frame');
 | |
| 			$recordFrame->addAttribute('posn', '0 ' . $y);
 | |
| 			
 | |
| 			// Background
 | |
| 			$quad = $recordFrame->addChild('quad');
 | |
| 			Tools::addAlignment($quad);
 | |
| 			$quad->addAttribute('sizen', $width . ' ' . $line_height);
 | |
| 			$quad->addAttribute('style', 'Bgs1InRace');
 | |
| 			$quad->addAttribute('substyle', 'BgTitleGlow');
 | |
| 			
 | |
| 			// Rank
 | |
| 			$label = $recordFrame->addChild('label');
 | |
| 			Tools::addAlignment($label, 'left');
 | |
| 			$label->addAttribute('posn', ($width * -0.47) . ' 0');
 | |
| 			$label->addAttribute('sizen', ($width * 0.06) . ' ' . $line_height);
 | |
| 			$label->addAttribute('textsize', '1');
 | |
| 			$label->addAttribute('textprefix', '$o');
 | |
| 			$label->addAttribute('text', $record['rank']);
 | |
| 			
 | |
| 			// Name
 | |
| 			$label = $recordFrame->addChild('label');
 | |
| 			Tools::addAlignment($label, 'left');
 | |
| 			$label->addAttribute('posn', ($width * -0.4) . ' 0');
 | |
| 			$label->addAttribute('sizen', ($width * 0.6) . ' ' . $line_height);
 | |
| 			$label->addAttribute('textsize', '1');
 | |
| 			$label->addAttribute('text', $record['NickName']);
 | |
| 			
 | |
| 			// Time
 | |
| 			$label = $recordFrame->addChild('label');
 | |
| 			Tools::addAlignment($label, 'right');
 | |
| 			$label->addAttribute('posn', ($width * 0.47) . ' 0');
 | |
| 			$label->addAttribute('sizen', ($width * 0.25) . ' ' . $line_height);
 | |
| 			$label->addAttribute('textsize', '1');
 | |
| 			$label->addAttribute('text', Tools::formatTime($record['time']));
 | |
| 			
 | |
| 			$index++;
 | |
| 		}
 | |
| 		
 | |
| 		return $xml;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Update dedimania records manialink
 | |
| 	 */
 | |
| 	private function buildDedimaniaManialink() {
 | |
| 		if (!isset($this->dedimaniaData['records'])) {
 | |
| 			return;
 | |
| 		}
 | |
| 		$records = $this->dedimaniaData['records']['Records'];
 | |
| 		
 | |
| 		$pos_x = (float) $this->config->dedimania_records->widget->pos_x;
 | |
| 		$pos_y = (float) $this->config->dedimania_records->widget->pos_y;
 | |
| 		$title = (string) $this->config->dedimania_records->widget->title;
 | |
| 		$width = (float) $this->config->dedimania_records->widget->width;
 | |
| 		$lines = (int) $this->config->dedimania_records->widget->lines;
 | |
| 		$line_height = (float) $this->config->dedimania_records->widget->line_height;
 | |
| 		
 | |
| 		$xml = Tools::newManialinkXml(self::MLID_DEDI);
 | |
| 		
 | |
| 		$frame = $xml->addChild('frame');
 | |
| 		$frame->addAttribute('posn', $pos_x . ' ' . $pos_y);
 | |
| 		
 | |
| 		// Background
 | |
| 		$quad = $frame->addChild('quad');
 | |
| 		Tools::addAlignment($quad, 'center', 'top');
 | |
| 		$quad->addAttribute('sizen', ($width * 1.05) . ' ' . (7. + $lines * $line_height));
 | |
| 		$quad->addAttribute('style', 'Bgs1InRace');
 | |
| 		$quad->addAttribute('substyle', 'BgTitleShadow');
 | |
| 		
 | |
| 		// Title
 | |
| 		$label = $frame->addChild('label');
 | |
| 		Tools::addAlignment($label);
 | |
| 		Tools::addTranslate($xml);
 | |
| 		$label->addAttribute('posn', '0 ' . ($line_height * -0.9));
 | |
| 		$label->addAttribute('sizen', $width . ' 0');
 | |
| 		$label->addAttribute('style', 'TextTitle1');
 | |
| 		$label->addAttribute('textsize', '2');
 | |
| 		$label->addAttribute('text', $title);
 | |
| 		
 | |
| 		// Times
 | |
| 		foreach ($records as $index => $record) {
 | |
| 			$y = -8. - $index * $line_height;
 | |
| 			
 | |
| 			$recordFrame = $frame->addChild('frame');
 | |
| 			$recordFrame->addAttribute('posn', '0 ' . $y);
 | |
| 			
 | |
| 			// Background
 | |
| 			$quad = $recordFrame->addChild('quad');
 | |
| 			Tools::addAlignment($quad);
 | |
| 			$quad->addAttribute('sizen', $width . ' ' . $line_height);
 | |
| 			$quad->addAttribute('style', 'Bgs1InRace');
 | |
| 			$quad->addAttribute('substyle', 'BgTitleGlow');
 | |
| 			
 | |
| 			// Rank
 | |
| 			$label = $recordFrame->addChild('label');
 | |
| 			Tools::addAlignment($label, 'left');
 | |
| 			$label->addAttribute('posn', ($width * -0.47) . ' 0');
 | |
| 			$label->addAttribute('sizen', ($width * 0.06) . ' ' . $line_height);
 | |
| 			$label->addAttribute('textsize', '1');
 | |
| 			$label->addAttribute('textprefix', '$o');
 | |
| 			$label->addAttribute('text', $record['Rank']);
 | |
| 			
 | |
| 			// Name
 | |
| 			$label = $recordFrame->addChild('label');
 | |
| 			Tools::addAlignment($label, 'left');
 | |
| 			$label->addAttribute('posn', ($width * -0.4) . ' 0');
 | |
| 			$label->addAttribute('sizen', ($width * 0.6) . ' ' . $line_height);
 | |
| 			$label->addAttribute('textsize', '1');
 | |
| 			$label->addAttribute('text', $record['NickName']);
 | |
| 			
 | |
| 			// Time
 | |
| 			$label = $recordFrame->addChild('label');
 | |
| 			Tools::addAlignment($label, 'right');
 | |
| 			$label->addAttribute('posn', ($width * 0.47) . ' 0');
 | |
| 			$label->addAttribute('sizen', ($width * 0.25) . ' ' . $line_height);
 | |
| 			$label->addAttribute('textsize', '1');
 | |
| 			$label->addAttribute('text', Tools::formatTime($record['Best']));
 | |
| 			
 | |
| 			if ($index >= $lines - 1) break;
 | |
| 		}
 | |
| 		
 | |
| 		return $xml;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Fetch local records for the given map
 | |
| 	 *
 | |
| 	 * @param string $mapUId        	
 | |
| 	 * @param int $limit        	
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	private function getLocalRecords($mapUId, $limit = -1) {
 | |
| 		$query = "SELECT * FROM (
 | |
| 				SELECT recs.*, @rank := @rank + 1 as `rank` FROM `" . self::TABLE_RECORDS . "` recs, (SELECT @rank := 0) ra
 | |
| 				WHERE recs.`mapUId` = '" . $this->iControl->database->escape($mapUId) . "'
 | |
| 				ORDER BY recs.`time` ASC
 | |
| 				" . ($limit > 0 ? "LIMIT " . $limit : "") . ") records
 | |
| 			LEFT JOIN `" . Database::TABLE_PLAYERS . "` players
 | |
| 			ON records.`Login` = players.`Login`;";
 | |
| 		return $this->iControl->database->query($query);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Retrieve the local record for the given map and login
 | |
| 	 *
 | |
| 	 * @param string $mapUId        	
 | |
| 	 * @param string $login        	
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	private function getLocalRecord($mapUId, $login) {
 | |
| 		if (!$mapUId || !$login) return null;
 | |
| 		$database = $this->iControl->database;
 | |
| 		$query = "SELECT records.* FROM (
 | |
| 			SELECT recs.*, @rank := @rank + 1 as `rank` FROM `" . self::TABLE_RECORDS . "` `recs`, (SELECT @rank := 0) r
 | |
| 			WHERE recs.`mapUid` = '" . $database->escape($mapUId) . "'
 | |
| 			ORDER BY recs.`time` ASC) `records`
 | |
| 			WHERE records.`Login` = '" . $database->escape($login) . "';";
 | |
| 		$result = $database->query($query);
 | |
| 		if (!$result || !is_object($result)) {
 | |
| 			trigger_error("Couldn't retrieve player record for '" . $login . "'." . $database->mysqli->error);
 | |
| 			return null;
 | |
| 		}
 | |
| 		while ($record = $result->fetch_assoc()) {
 | |
| 			return $record;
 | |
| 		}
 | |
| 		return null;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Encodes the given xml rpc method and params
 | |
| 	 *
 | |
| 	 * @param string $method        	
 | |
| 	 * @param array $params        	
 | |
| 	 * @return string
 | |
| 	 */
 | |
| 	private function encode_request($method, $params) {
 | |
| 		$paramArray = array(array('methodName' => $method, 'params' => $params), 
 | |
| 			array('methodName' => self::DEDIMANIA_WARNINGSANDTTR2, 'params' => array()));
 | |
| 		return xmlrpc_encode_request(self::XMLRPC_MULTICALL, array($paramArray), array('encoding' => 'UTF-8', 'escaping' => 'markup'));
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Decodes xml rpc response
 | |
| 	 *
 | |
| 	 * @param string $response        	
 | |
| 	 * @return mixed
 | |
| 	 */
 | |
| 	private function decode($response) {
 | |
| 		return xmlrpc_decode($response, 'utf-8');
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Handles xml rpc fault
 | |
| 	 *
 | |
| 	 * @param struct $fault        	
 | |
| 	 */
 | |
| 	private function handleXmlRpcFault($fault) {
 | |
| 		trigger_error('XmlRpc Fault: ' . $fault['faultString'] . ' (' . $fault['faultCode'] . ')');
 | |
| 	}
 | |
| }
 | |
| 
 | |
| ?>
 |