diff --git a/application/core/Statistics/StatisticManager.php b/application/core/Statistics/StatisticManager.php index 84e6a836..95ede3ad 100644 --- a/application/core/Statistics/StatisticManager.php +++ b/application/core/Statistics/StatisticManager.php @@ -91,13 +91,15 @@ class StatisticManager { } /** - * Get All statistics orderd by an given name + * Get All statistics ordered by an given name * - * @param $orderedBy - * @param $serverIndex + * @param string $statName + * @param $serverIndex + * @param $minValue + * @internal param $orderedBy * @return object */ - public function getStatsRanking($statName = '', $serverIndex = -1) { + public function getStatsRanking($statName = '', $serverIndex = -1, $minValue = -1) { if (isset($this->specialStats[$statName])) { return $this->getStatsRankingOfSpecialStat($statName, $serverIndex); } @@ -105,7 +107,11 @@ class StatisticManager { $mysqli = $this->maniaControl->database->mysqli; $statId = $this->getStatId($statName); - $query = "SELECT playerId, serverIndex, value FROM `" . self::TABLE_STATISTICS . "` WHERE statId = " . $statId . " ORDER BY value DESC LIMIT 100;"; + if ($minValue == -1) { + $query = "SELECT playerId, serverIndex, value FROM `" . self::TABLE_STATISTICS . "` WHERE statId = " . $statId . " ORDER BY value DESC LIMIT 100;"; + } else { + $query = "SELECT playerId, serverIndex, value FROM `" . self::TABLE_STATISTICS . "` WHERE statId = " . $statId . " AND value >= " . $minValue . " ORDER BY value DESC;"; + } $result = $mysqli->query($query); if (!$result) { diff --git a/application/plugins/InstagibRanking.php b/application/plugins/InstagibRanking.php deleted file mode 100644 index 3c1b230b..00000000 --- a/application/plugins/InstagibRanking.php +++ /dev/null @@ -1,299 +0,0 @@ -maniaControl = $maniaControl; - return; - $this->initTables(); - //TODO for all modes, rank by records, points or accuracy / kill-death - $this->maniaControl->callbackManager->registerCallbackListener(PlayerManager::CB_PLAYERJOINED, $this, 'handlePlayerConnect'); - $this->maniaControl->callbackManager->registerCallbackListener(CallbackManager::CB_MC_ENDMAP, $this, 'handleEndMap'); - - $maniaControl->settingManager->initSetting($this, self::SETTING_MIN_HITS_RATIO_RANKING, 100); - } - - /** - * Unload the plugin and its resources - */ - public function unload() { - // TODO: Implement unload() method. - } - - /** - * Get plugin id - * - * @return int - */ - public static function getId() { - return self::PLUGIN_ID; - } - - /** - * Get Plugin Name - * - * @return string - */ - public static function getName() { - return self::PLUGIN_NAME; - } - - /** - * Get Plugin Version - * - * @return float - */ - public static function getVersion() { - return self::PLUGIN_VERSION; - } - - /** - * Get Plugin Author - * - * @return string - */ - public static function getAuthor() { - return self::PLUGIN_AUTHOR; - } - - /** - * Get Plugin Description - * - * @return string - */ - public static function getDescription() { - // TODO: Implement getDescription() method. - } - - /** - * Create necessary database tables - */ - private function initTables() { - $mysqli = $this->maniaControl->database->mysqli; - $query = "CREATE TABLE IF NOT EXISTS `" . self::TABLE_RANK . "` ( - `PlayerIndex` mediumint(9) NOT NULL default 0, - `Rank` mediumint(9) NOT NULL default 0, - `Avg` float NOT NULL default 0, - KEY `PlayerIndex` (`PlayerIndex`), - UNIQUE `Rank` (`Rank`) - ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Mania Control Serverranking';"; - $mysqli->query($query); - if ($mysqli->error) { - trigger_error($mysqli->error, E_USER_ERROR); - } - } - - /** - * Resets and rebuilds the Ranking - */ - private function resetRanks() { - $mysqli = $this->maniaControl->database->mysqli; - - // Erase old Average Data - $mysqli->query('TRUNCATE TABLE ' . self::TABLE_RANK); - - //TODO setting minrank, maxrecs - - //$mapCnt = count($this->maniaControl->mapManager->getMaps()); - //TODO other modes, records and points - - $hits = $this->maniaControl->statisticManager->getStatsRanking(StatisticCollector::STAT_ON_HIT); - $killDeathRatios = $this->maniaControl->statisticManager->getStatsRanking(StatisticManager::SPECIAL_STAT_KD_RATIO); - $accuracies = $this->maniaControl->statisticManager->getStatsRanking(StatisticManager::SPECIAL_STAT_LASER_ACC); - - $minHits = $this->maniaControl->settingManager->getSetting($this, self::SETTING_MIN_HITS_RATIO_RANKING); - - $ranks = array(); - foreach($killDeathRatios as $player => $killDeathRatio) { - //TODO setting - if ($hits[$player] < $minHits || !isset($accuracies[$player])) { - continue; - } - $ranks[$player] = $killDeathRatio * $accuracies[$player]; - } - - arsort($ranks); - //TODO order desc / asc - - if (empty($ranks)) { - return; - } - - $this->recordCount = count($ranks); - - //Compute each player's new average score - $query = "INSERT INTO " . self::TABLE_RANK . " VALUES "; - $i = 1; - - foreach($ranks as $player => $rankValue) { - $query .= '(' . $player . ',' . $i . ',' . $rankValue . '),'; - $i++; - } - $query = substr($query, 0, strlen($query) - 1); // strip trailing ',' - - $mysqli->query($query); - } - - /** - * Handle PlayerConnect callback - * - * @param array $callback - */ - public function handlePlayerConnect(array $callback) { - $login = $callback[1][0]; - $player = $this->maniaControl->playerManager->getPlayer($login); - if (!$player) { - return; - } - - $this->showRank($player); - $this->showNextRank($player); - } - - /** - * Shows Ranks on endMap - * - * @param array $callback - */ - public function handleEndMap(array $callback) { - $this->resetRanks(); - - foreach($this->maniaControl->playerManager->getPlayers() as $player) { - $this->showRank($player); - $this->showNextRank($player); - } - //TODO cb on rank builded - } - - /** - * Shows the serverRank to a certain Player - * - * @param Player $player - */ - public function showRank(Player $player) { - $rankObj = $this->getRank($player); - - if ($rankObj != null) { - $message = '$0f3Your Server rank is $<$ff3' . $rankObj->rank . '$> / $<$fff' . $this->recordCount . '$> Ratio: $fff' . round($rankObj->avg, 2); - } else { - $minHits = $this->maniaControl->settingManager->getSetting($this, self::SETTING_MIN_HITS_RATIO_RANKING); - $message = '$0f3 You must make $<$fff' . $minHits . '$> Hits on this server before recieving a rank...'; - } - $this->maniaControl->chat->sendChat($message, $player->login); - } - - /** - * Gets A Rank As Object with properties Avg PlayerIndex and Rank - * - * @param Player $player - * @return Rank $rank - */ - private function getRank(Player $player) { - //TODO setting global from db or local - $mysqli = $this->maniaControl->database->mysqli; - - $result = $mysqli->query('SELECT * FROM ' . self::TABLE_RANK . ' WHERE PlayerIndex=' . $player->index); - if ($result->num_rows > 0) { - $row = $result->fetch_array(); - $result->free_result(); - return Rank::fromArray($row); - } else { - $result->free_result(); - return null; - } - } - - /** - * Get the Next Ranked Player - * - * @param Player $player - * @return Rank - */ - private function getNextRank(Player $player) { - $mysqli = $this->maniaControl->database->mysqli; - $rankObject = $this->getRank($player); - $nextRank = $rankObject->rank - 1; - - $result = $mysqli->query('SELECT * FROM ' . self::TABLE_RANK . ' WHERE Rank=' . $nextRank); - if ($result->num_rows > 0) { - $row = $result->fetch_array(); - $result->free_result(); - return Rank::fromArray($row); - } else { - $result->free_result(); - return null; - } - } - - - //TODO chatcommand showrank - - /** - * Shows which Player is next ranked to you - * - * @param Player $player - */ - public function showNextRank(Player $player) { - //TODO chatcommand - $rankObject = $this->getRank($player); - - if ($rankObject != null) { - if ($rankObject->rank > 1) { - $nextRank = $this->getNextRank($player); - $nextPlayer = $this->maniaControl->playerManager->getPlayerByIndex($nextRank->playerIndex); - $message = '$0f3The next better ranked player is $fff' . $nextPlayer->nickname; - } else { - $message = '$0f3No better ranked player :-)'; - } - $this->maniaControl->chat->sendChat($message, $player->login); - } - } -} - -class Rank extends AbstractStructure { - public $playerIndex; - public $rank; - public $avg; -} \ No newline at end of file diff --git a/application/plugins/LocalRecords.php b/application/plugins/LocalRecords.php index 3ab69f4d..5fbf6a9a 100644 --- a/application/plugins/LocalRecords.php +++ b/application/plugins/LocalRecords.php @@ -393,7 +393,7 @@ class LocalRecordsPlugin implements CallbackListener, TimerListener, Plugin { * @param int $limit * @return array */ - private function getLocalRecords(Map $map, $limit = -1) { + public function getLocalRecords(Map $map, $limit = -1) { $mysqli = $this->maniaControl->database->mysqli; $limit = ($limit > 0 ? "LIMIT " . $limit : ""); $query = "SELECT * FROM ( diff --git a/application/plugins/ServerRanking.php b/application/plugins/ServerRanking.php new file mode 100644 index 00000000..89ea0591 --- /dev/null +++ b/application/plugins/ServerRanking.php @@ -0,0 +1,409 @@ +maniaControl = $maniaControl; + + $this->initTables(); + + $this->maniaControl->callbackManager->registerCallbackListener(PlayerManager::CB_PLAYERJOINED, $this, 'handlePlayerConnect'); + $this->maniaControl->callbackManager->registerCallbackListener(CallbackManager::CB_MC_ENDMAP, $this, 'handleEndMap'); + + $maniaControl->settingManager->initSetting($this, self::SETTING_MIN_HITS_RATIO_RANKING, 100); + $maniaControl->settingManager->initSetting($this, self::SETTING_MIN_HITS_HITS_RANKING, 15); + + $maniaControl->settingManager->initSetting($this, self::SETTING_MIN_REQUIRED_RECORDS, 3); + $maniaControl->settingManager->initSetting($this, self::SETTING_MAX_STORED_RECORDS, 50); + + $titleId = $this->maniaControl->server->titleId; + $titlePrefix = strtolower(substr($titleId, 0, 2)); + if ($titlePrefix == 'tm') { //TODO also add obstacle here as default + $maniaControl->settingManager->initSetting($this, self::SETTING_MIN_RANKING_TYPE, self::RANKING_TYPE_RECORDS); + } else if ($this->maniaControl->client->getScriptName() == "InstaDM.Script.txt") { + $maniaControl->settingManager->initSetting($this, self::SETTING_MIN_RANKING_TYPE, self::RANKING_TYPE_RATIOS); + } else { + $maniaControl->settingManager->initSetting($this, self::SETTING_MIN_RANKING_TYPE, self::RANKING_TYPE_HITS); + } + + //Check if the type is Correct + $type = $this->maniaControl->settingManager->getSetting($this, self::SETTING_MIN_RANKING_TYPE); + if ($type != self::RANKING_TYPE_RECORDS && $type != self::RANKING_TYPE_HITS && $type != self::RANKING_TYPE_RATIOS) { + $error = 'Ranking Type is not correct, possible values(' . self::RANKING_TYPE_RATIOS . ', ' . self::RANKING_TYPE_HITS . ', ' . self::RANKING_TYPE_HITS . ')'; + throw new Exception($error); + } + } + + /** + * Unload the plugin and its resources + */ + public function unload() { + $this->maniaControl->callbackManager->unregisterCallbackListener($this); + } + + /** + * Get plugin id + * + * @return int + */ + public static function getId() { + return self::PLUGIN_ID; + } + + /** + * Get Plugin Name + * + * @return string + */ + public static function getName() { + return self::PLUGIN_NAME; + } + + /** + * Get Plugin Version + * + * @return float + */ + public static function getVersion() { + return self::PLUGIN_VERSION; + } + + /** + * Get Plugin Author + * + * @return string + */ + public static function getAuthor() { + return self::PLUGIN_AUTHOR; + } + + /** + * Get Plugin Description + * + * @return string + */ + public static function getDescription() { + return "ServerRanking Plugin, Serverranking by an avg build from the records, per count of hits, or by a multiplication from Kill/Death Ratio and Laser accuracy"; + } + + /** + * Create necessary database tables + */ + private function initTables() { + $mysqli = $this->maniaControl->database->mysqli; + $query = "CREATE TABLE IF NOT EXISTS `" . self::TABLE_RANK . "` ( + `PlayerIndex` mediumint(9) NOT NULL default 0, + `Rank` mediumint(9) NOT NULL default 0, + `Avg` float NOT NULL default 0, + KEY `PlayerIndex` (`PlayerIndex`), + UNIQUE `Rank` (`Rank`) + ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Mania Control Serverranking';"; + $mysqli->query($query); + if ($mysqli->error) { + trigger_error($mysqli->error, E_USER_ERROR); + } + } + + /** + * Resets and rebuilds the Ranking + */ + private function resetRanks() { + $mysqli = $this->maniaControl->database->mysqli; + + // Erase old Average Data + $mysqli->query('TRUNCATE TABLE ' . self::TABLE_RANK); + $type = $this->maniaControl->settingManager->getSetting($this, self::SETTING_MIN_RANKING_TYPE); + + switch($type) { + case self::RANKING_TYPE_RATIOS: + $minHits = $this->maniaControl->settingManager->getSetting($this, self::SETTING_MIN_HITS_RATIO_RANKING); + + $hits = $this->maniaControl->statisticManager->getStatsRanking(StatisticCollector::STAT_ON_HIT, -1 , $minHits); + $killDeathRatios = $this->maniaControl->statisticManager->getStatsRanking(StatisticManager::SPECIAL_STAT_KD_RATIO); + $accuracies = $this->maniaControl->statisticManager->getStatsRanking(StatisticManager::SPECIAL_STAT_LASER_ACC); + + $ranks = array(); + foreach($hits as $player => $hitCount) { + if (!isset($killDeathRatios[$player]) || !isset($accuracies[$player])) { + continue; + } + $ranks[$player] = $killDeathRatios[$player] * $accuracies[$player]; + } + + arsort($ranks); + + break; + case self::RANKING_TYPE_HITS: + $minHits = $this->maniaControl->settingManager->getSetting($this, self::SETTING_MIN_HITS_HITS_RANKING); + + $ranks = $this->maniaControl->statisticManager->getStatsRanking(StatisticCollector::STAT_ON_HIT, -1, $minHits); + + arsort($ranks); + break; + case self::RANKING_TYPE_RECORDS: //TODO verify workable status + if (!$this->maniaControl->pluginManager->isPluginActive('LocalRecordsPlugin')) { + return; + } + + $requiredRecords = $this->maniaControl->settingManager->getSetting($this, self::SETTING_MIN_REQUIRED_RECORDS); + $maxRecords = $this->maniaControl->settingManager->getSetting($this, self::SETTING_MAX_STORED_RECORDS); + + $query = 'SELECT playerIndex, COUNT(*) AS Cnt + FROM ' . LocalRecordsPlugin::TABLE_RECORDS . ' + GROUP BY PlayerId + HAVING Cnt >=' . $requiredRecords; + $result = $mysqli->query($query); + $players = array(); + while($row = $result->fetch_object()) { + $players[$row->playerIndex] = array(0, 0); //sum, count + } + $result->free_result(); + + /** @var LocalRecordsPlugin $localRecordsPlugin */ + $localRecordsPlugin = $this->maniaControl->pluginManager->getPlugin('LocalRecordsPlugin'); + $maps = $this->maniaControl->mapManager->getMaps(); + foreach($maps as $map) { + $records = $localRecordsPlugin->getLocalRecords($map, $maxRecords); + + $i = 1; + foreach($records as $record) { + if (isset($players[$record->playerIndex])) { + $players[$record->playerIndex][0] += $i; + $players[$record->playerIndex][1]++; + } + $i++; + } + } + + $mapCount = count($maps); + + //compute each players new average score + $ranks = array(); + foreach($players as $player => $val) { + $sum = $val[0]; + $cnt = $val[1]; + // ranked maps sum + $maxRecs rank for all remaining maps + $ranks[$player] = ($sum + ($mapCount - $cnt) * $maxRecords) / $mapCount; + } + + //TODO improve statement: + array_reverse(arsort($ranks)); + break; + } + + if (empty($ranks)) { + return; + } + + $this->recordCount = count($ranks); + + //Compute each player's new average score + $query = "INSERT INTO " . self::TABLE_RANK . " VALUES "; + $i = 1; + + foreach($ranks as $player => $rankValue) { + $query .= '(' . $player . ',' . $i . ',' . $rankValue . '),'; + $i++; + } + $query = substr($query, 0, strlen($query) - 1); // strip trailing ',' + + $mysqli->query($query); + } + + + /** + * Handle PlayerConnect callback + * + * @param array $callback + */ + public function handlePlayerConnect(array $callback) { + $login = $callback[1][0]; + $player = $this->maniaControl->playerManager->getPlayer($login); + if (!$player) { + return; + } + + $this->showRank($player); + $this->showNextRank($player); + } + + /** + * Shows Ranks on endMap + * + * @param array $callback + */ + public function handleEndMap(array $callback) { + $this->resetRanks(); + + foreach($this->maniaControl->playerManager->getPlayers() as $player) { + $this->showRank($player); + $this->showNextRank($player); + } + //TODO cb on rank builded + } + + /** + * Shows the serverRank to a certain Player + * + * @param Player $player + */ + public function showRank(Player $player) { + $rankObj = $this->getRank($player); + + $type = $this->maniaControl->settingManager->getSetting($this, self::SETTING_MIN_RANKING_TYPE); + + $message = ''; + if ($rankObj != null) { + switch($type) { + case self::RANKING_TYPE_RATIOS: + $message = '$0f3Your Server rank is $<$ff3' . $rankObj->rank . '$> / $<$fff' . $this->recordCount . '$> Ratio: $fff' . round($rankObj->avg, 2); + break; + case self::RANKING_TYPE_HITS: + $message = '$0f3Your Server rank is $<$ff3' . $rankObj->rank . '$> / $<$fff' . $this->recordCount . '$> Hits: $fff' . $rankObj->avg; + break; + case self::RANKING_TYPE_RECORDS: + $message = '$0f3Your Server rank is $<$ff3' . $rankObj->rank . '$> / $<$fff' . $this->recordCount . '$> Avg: $fff' . round($rankObj->avg, 2); + } + } else { + switch($type) { + case self::RANKING_TYPE_RATIOS: + $minHits = $this->maniaControl->settingManager->getSetting($this, self::SETTING_MIN_HITS_RATIO_RANKING); + $message = '$0f3 You must make $<$fff' . $minHits . '$> Hits on this server before recieving a rank...'; + break; + case self::RANKING_TYPE_HITS: + $minHits = $this->maniaControl->settingManager->getSetting($this, self::SETTING_MIN_HITS_HITS_RANKING); + $message = '$0f3 You must make $<$fff' . $minHits . '$> Hits on this server before recieving a rank...'; + break; + case self::RANKING_TYPE_RECORDS: + $minHits = $this->maniaControl->settingManager->getSetting($this, self::SETTING_MIN_REQUIRED_RECORDS); + $message = '$0f3 You need $<$fff' . $minHits . '$> Records on this server before recieving a rank...'; + } + } + $this->maniaControl->chat->sendChat($message, $player->login); + } + + /** + * Gets A Rank As Object with properties Avg PlayerIndex and Rank + * + * @param Player $player + * @return Rank $rank + */ + private function getRank(Player $player) { + //TODO setting global from db or local + $mysqli = $this->maniaControl->database->mysqli; + + $result = $mysqli->query('SELECT * FROM ' . self::TABLE_RANK . ' WHERE PlayerIndex=' . $player->index); + if ($result->num_rows > 0) { + $row = $result->fetch_array(); + $result->free_result(); + return Rank::fromArray($row); + } else { + $result->free_result(); + return null; + } + } + + /** + * Get the Next Ranked Player + * + * @param Player $player + * @return Rank + */ + private function getNextRank(Player $player) { + $mysqli = $this->maniaControl->database->mysqli; + $rankObject = $this->getRank($player); + $nextRank = $rankObject->rank - 1; + + $result = $mysqli->query('SELECT * FROM ' . self::TABLE_RANK . ' WHERE Rank=' . $nextRank); + if ($result->num_rows > 0) { + $row = $result->fetch_array(); + $result->free_result(); + return Rank::fromArray($row); + } else { + $result->free_result(); + return null; + } + } + + + //TODO chatcommand showrank + + /** + * Shows which Player is next ranked to you + * + * @param Player $player + */ + public function showNextRank(Player $player) { + //TODO chatcommand + $rankObject = $this->getRank($player); + + if ($rankObject != null) { + if ($rankObject->rank > 1) { + $nextRank = $this->getNextRank($player); + $nextPlayer = $this->maniaControl->playerManager->getPlayerByIndex($nextRank->playerIndex); + $message = '$0f3The next better ranked player is $fff' . $nextPlayer->nickname; + } else { + $message = '$0f3No better ranked player :-)'; + } + $this->maniaControl->chat->sendChat($message, $player->login); + } + } +} + +/** + * Rank Structure + */ +class Rank extends AbstractStructure { + public $playerIndex; + public $rank; + public $avg; +} \ No newline at end of file