diff --git a/application/core/Maps/Map.php b/application/core/Maps/Map.php index fb822842..98d380d5 100644 --- a/application/core/Maps/Map.php +++ b/application/core/Maps/Map.php @@ -14,8 +14,7 @@ class Map { /** * Public properties */ - public $mapFetcher = null; - public $id = 0; + public $index = -1; public $name = 'undefined'; public $uid = ''; public $fileName = ''; @@ -32,6 +31,7 @@ class Map { public $comment = ''; public $titleUid = ''; public $startTime = 0; + public $mapFetcher = null; /** * Private properties diff --git a/application/core/Maps/MapManager.php b/application/core/Maps/MapManager.php index d7f46fcc..13438184 100644 --- a/application/core/Maps/MapManager.php +++ b/application/core/Maps/MapManager.php @@ -36,9 +36,14 @@ class MapManager implements CallbackListener { */ public function __construct(ManiaControl $maniaControl) { $this->maniaControl = $maniaControl; + + // Init database tables $this->initTables(); + + // Create map commands instance $this->mapCommands = new MapCommands($maniaControl); + // Register for callbacks $this->maniaControl->callbackManager->registerCallbackListener(CallbackManager::CB_MC_BEGINMAP, $this, 'handleBeginMap'); } @@ -49,7 +54,7 @@ class MapManager implements CallbackListener { */ private function initTables() { $mysqli = $this->maniaControl->database->mysqli; - $mapsTableQuery = "CREATE TABLE IF NOT EXISTS `" . self::TABLE_MAPS . "` ( + $query = "CREATE TABLE IF NOT EXISTS `" . self::TABLE_MAPS . "` ( `index` int(11) NOT NULL AUTO_INCREMENT, `uid` varchar(50) COLLATE utf8_unicode_ci NOT NULL, `name` varchar(150) COLLATE utf8_unicode_ci NOT NULL, @@ -61,18 +66,12 @@ class MapManager implements CallbackListener { PRIMARY KEY (`index`), UNIQUE KEY `uid` (`uid`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Map data' AUTO_INCREMENT=1;"; - $mapsTableStatement = $mysqli->prepare($mapsTableQuery); + $result = $mysqli->query($query); if ($mysqli->error) { trigger_error($mysqli->error, E_USER_ERROR); return false; } - $mapsTableStatement->execute(); - if ($mapsTableStatement->error) { - trigger_error($mapsTableStatement->error, E_USER_ERROR); - return false; - } - $mapsTableStatement->close(); - return true; + return $result; } /** @@ -81,7 +80,7 @@ class MapManager implements CallbackListener { * @param \ManiaControl\Maps\Map $map * @return boolean */ - private function saveMap(Map $map) { + private function saveMap(Map &$map) { $mysqli = $this->maniaControl->database->mysqli; $mapQuery = "INSERT INTO `" . self::TABLE_MAPS . "` ( `uid`, @@ -106,6 +105,7 @@ class MapManager implements CallbackListener { $mapStatement->close(); return false; } + $map->index = $mapStatement->insert_id; $mapStatement->close(); return true; } @@ -134,6 +134,7 @@ class MapManager implements CallbackListener { } $rpcMap = $this->maniaControl->client->getResponse(); $map = new Map($this->maniaControl, $rpcMap); + $this->addMap($map); return $map; } @@ -149,4 +150,4 @@ class MapManager implements CallbackListener { } $this->addMap($map); } -} \ No newline at end of file +} diff --git a/application/core/database.php b/application/core/database.php index 2d993eac..804afec7 100644 --- a/application/core/database.php +++ b/application/core/database.php @@ -8,12 +8,6 @@ namespace ManiaControl; * @author steeffeen & kremsy */ class Database { - /** - * Constants - */ - const TABLE_PLAYERS = 'mc_players'; - const TABLE_MAPS = 'mc_maps'; - /** * Public properties */ diff --git a/application/plugins/LocalRecords.php b/application/plugins/LocalRecords.php new file mode 100644 index 00000000..7c4f8fb2 --- /dev/null +++ b/application/plugins/LocalRecords.php @@ -0,0 +1,354 @@ +maniaControl = $maniaControl; + + // Init tables + $this->initTables(); + + // Register for callbacks + $this->maniaControl->callbackManager->registerCallbackListener(CallbackManager::CB_MC_ONINIT, $this, 'handleOnInit'); + $this->maniaControl->callbackManager->registerCallbackListener(CallbackManager::CB_MC_1_SECOND, $this, 'handle1Second'); + $this->maniaControl->callbackManager->registerCallbackListener(CallbackManager::CB_MC_BEGINMAP, $this, 'handleMapBegin'); + $this->maniaControl->callbackManager->registerCallbackListener(CallbackManager::CB_MC_CLIENTUPDATED, $this, + 'handleClientUpdated'); + $this->maniaControl->callbackManager->registerCallbackListener(CallbackManager::CB_TM_PLAYERFINISH, $this, + 'handlePlayerFinish'); + } + + /** + * Initialize needed database tables + */ + private function initTables() { + $mysqli = $this->maniaControl->database->mysqli; + $query = "CREATE TABLE IF NOT EXISTS `" . self::TABLE_RECORDS . "` ( + `index` int(11) NOT NULL AUTO_INCREMENT, + `mapIndex` int(11) NOT NULL, + `playerIndex` int(11) 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` (`mapIndex`,`playerIndex`) + ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1;"; + if (!$mysqli->query($query)) { + trigger_error("Couldn't create records table. " . $mysqli->error, E_USER_ERROR); + } + } + + /** + * Handle ManiaControl init + * + * @param array $callback + */ + public function handleOnInit(array $callback) { + // Let manialinks update + $this->updateManialink = true; + } + + /** + * Handle 1Second callback + * + * @param array $callback + */ + public function handle1Second(array $callback) { + // Send records manialinks if needed + if ($this->updateManialink) { + $manialink = $this->buildLocalManialink(); + $this->sendManialink($manialink); + $this->updateManialink = false; + } + } + + /** + * Handle PlayerConnect callback + * + * @param array $callback + */ + public function handlePlayerConnect(array $callback) { + $this->updateManialink = true; + } + + /** + * Handle BeginMap callback + * + * @param array $callback + */ + public function handleMapBegin(array $callback) { + $this->updateManialink = true; + } + + /** + * Handle PlayerFinish callback + * + * @param array $callback + */ + public function handlePlayerFinish(array $callback) { + $data = $callback[1]; + if ($data[0] <= 0 || $data[2] <= 0) { + // Invalid player or time + return; + } + + $login = $data[1]; + $player = $this->maniaControl->playerManager->getPlayer($login); + if (!$player) { + // Invalid player + return; + } + + $time = $data[2]; + $map = $this->maniaControl->mapManager->getCurrentMap(); + + // Check old record of the player + $oldRecord = $this->getLocalRecord($map, $player); + if ($oldRecord) { + if ($oldRecord->time < $time) { + // Not improved + return; + } + if ($oldRecord->time == $time) { + // Same time + $message = '$<' . $player->nickname . '$> equalized her/his $<$o' . $oldRecord->rank . '.$> Local Record: ' . + Formatter::formatTime($oldRecord->time); + $this->maniaControl->chat->sendInformation($message); + return; + } + } + + // Save time + $mysqli = $this->maniaControl->database->mysqli; + $query = "INSERT INTO `" . self::TABLE_RECORDS . "` ( + `mapIndex`, + `playerIndex`, + `time` + ) VALUES ( + {$map->index}, + {$player->index}, + {$time} + ) ON DUPLICATE KEY UPDATE + `time` = VALUES(`time`);"; + $result = $mysqli->query($query); + if ($mysqli->error) { + trigger_error($mysqli->error); + return; + } + $this->updateManialink = true; + + // Announce record + $newRecord = $this->getLocalRecord($map, $player); + if (!$oldRecord || $newRecord->rank < $oldRecord->rank) { + $improvement = 'gained the'; + } + else { + $improvement = 'improved her/his'; + } + $message = '$<' . $player->nickname . '$> ' . $improvement . ' $<$o' . $newRecord->rank . '.$> Local Record: ' . + Formatter::formatTime($newRecord->time); + $this->maniaControl->chat->sendInformation($message); + } + + /** + * Send manialink to clients + * + * @param string $manialink + * @param string $login + */ + private function sendManialink($manialink, $login = null) { + if ($login) { + if (!$this->maniaControl->client->query('SendDisplayManialinkPageToLogin', $login, $manialink, 0, false)) { + trigger_error("Couldn't send manialink to player '{$login}'. " . $this->maniaControl->getClientErrorText()); + } + return; + } + if (!$this->maniaControl->client->query('SendDisplayManialinkPage', $manialink, 0, false)) { + trigger_error("Couldn't send manialink to players. " . $this->maniaControl->getClientErrorText()); + } + } + + /** + * Handle ClientUpdated callback + * + * @param array $data + */ + public function handleClientUpdated(array $callback) { + $this->updateManialink = true; + } + + /** + * Build the local records manialink + * + * @return string + */ + private function buildLocalManialink() { + $map = $this->maniaControl->mapManager->getCurrentMap(); + if (!$map) { + return null; + } + + $pos_x = $this->maniaControl->settingManager->getSetting($this, 'Widget_PosX', -139.); + $pos_y = $this->maniaControl->settingManager->getSetting($this, 'Widget_PosY', 65.); + $title = $this->maniaControl->settingManager->getSetting($this, 'Widget_Title', 'Local Records'); + $width = $this->maniaControl->settingManager->getSetting($this, 'Widget_Width', 40.); + $lines = $this->maniaControl->settingManager->getSetting($this, 'Widget_LinesCount', 25); + $line_height = $this->maniaControl->settingManager->getSetting($this, 'Widget_LineHeight', 4.); + + $records = $this->getLocalRecords($map); + if (!is_array($records)) { + trigger_error("Couldn't fetch player records."); + return null; + } + + $manialink = new ManiaLink(self::MLID_RECORDS); + $frame = new Frame(); + $manialink->add($frame); + $frame->setPosition($pos_x, $pos_y); + + $backgroundQuad = new Quad(); + $frame->add($backgroundQuad); + $backgroundQuad->setVAlign(Control::TOP); + $backgroundQuad->setSize($width * 1.05, 7. + $lines * $line_height); + $backgroundQuad->setStyles('Bgs1InRace', 'BgTitleShadow'); + + $titleLabel = new Label(); + $frame->add($titleLabel); + // TODO: set translateable + $titleLabel->setPosition(0, $line_height * -0.9); + $titleLabel->setSize($width); + $titleLabel->setStyle(Label_Text::STYLE_TextTitle1); + $titleLabel->setTextSize(2); + $titleLabel->setText($title); + + // Times + foreach ($records as $index => $record) { + $y = -8. - $index * $line_height; + + $recordFrame = new Frame(); + $frame->add($recordFrame); + $recordFrame->setPosition(0, $y); + + $backgroundQuad = new Quad(); + $recordFrame->add($backgroundQuad); + $backgroundQuad->setSize($width, $line_height); + $backgroundQuad->setStyles('Bgs1InRace', 'BgTitleGlow'); + + $rankLabel = new Label(); + $recordFrame->add($rankLabel); + $rankLabel->setHAlign(Control::LEFT); + $rankLabel->setPosition($width * -0.47); + $rankLabel->setSize($width * 0.06, $line_height); + $rankLabel->setTextSize(1); + $rankLabel->setTextPrefix('$o'); + $rankLabel->setText($record->rank); + + $nameLabel = new Label(); + $recordFrame->add($nameLabel); + $nameLabel->setHAlign(Control::LEFT); + $nameLabel->setPosition($width * -0.4); + $nameLabel->setSize($width * 0.6, $line_height); + $nameLabel->setTextSize(1); + $nameLabel->setText($record->nickname); + + $timeLabel = new Label(); + $recordFrame->add($timeLabel); + $timeLabel->setHAlign(Control::RIGHT); + $timeLabel->setPosition($width * 0.47); + $timeLabel->setSize($width * 0.25, $line_height); + $timeLabel->setTextSize(1); + $timeLabel->setText(Formatter::formatTime($record->time)); + } + + return $manialink->render()->saveXML(); + } + + /** + * Fetch local records for the given map + * + * @param Map $map + * @param int $limit + * @return array + */ + private function getLocalRecords(Map $map, $limit = -1) { + $mysqli = $this->maniaControl->database->mysqli; + $limit = ($limit > 0 ? "LIMIT " . $limit : ""); + $query = "SELECT * FROM ( + SELECT recs.*, @rank := @rank + 1 as `rank` FROM `" . self::TABLE_RECORDS . "` recs, (SELECT @rank := 0) ra + WHERE recs.`mapIndex` = {$map->index} + ORDER BY recs.`time` ASC + {$limit}) records + LEFT JOIN `" . PlayerManager::TABLE_PLAYERS . "` players + ON records.`playerIndex` = players.`index`;"; + $result = $mysqli->query($query); + if ($mysqli->error) { + trigger_error($mysqli->error); + return null; + } + $records = array(); + while ($record = $result->fetch_object()) { + array_push($records, $record); + } + $result->free(); + return $records; + } + + /** + * Retrieve the local record for the given map and login + * + * @param Map $map + * @param Player $player + * @return mixed + */ + private function getLocalRecord(Map $map, Player $player) { + $mysqli = $this->maniaControl->database->mysqli; + $query = "SELECT records.* FROM ( + SELECT recs.*, @rank := @rank + 1 as `rank` FROM `" . self::TABLE_RECORDS . "` recs, (SELECT @rank := 0) ra + WHERE recs.`mapIndex` = {$map->index} + ORDER BY recs.`time` ASC) records + WHERE records.`playerIndex` = {$player->index};"; + $result = $mysqli->query($query); + if ($mysqli->error) { + trigger_error("Couldn't retrieve player record for '{$player->login}'." . $mysqli->error); + return null; + } + $record = $result->fetch_object(); + $result->free(); + return $record; + } +} + +?> diff --git a/application/plugins/Obstacle.php b/application/plugins/Obstacle.php index 0ec87841..553653f0 100644 --- a/application/plugins/Obstacle.php +++ b/application/plugins/Obstacle.php @@ -1,6 +1,7 @@