2014-02-06 14:35:54 +01:00
< ? php
use ManiaControl\Callbacks\CallbackListener ;
use ManiaControl\Callbacks\CallbackManager ;
2014-02-06 14:49:23 +01:00
use ManiaControl\Commands\CommandListener ;
2014-02-06 14:35:54 +01:00
use ManiaControl\ManiaControl ;
use ManiaControl\Players\Player ;
use ManiaControl\Players\PlayerManager ;
use ManiaControl\Plugins\Plugin ;
use ManiaControl\Statistics\StatisticCollector ;
use ManiaControl\Statistics\StatisticManager ;
use Maniaplanet\DedicatedServer\Structures\AbstractStructure ;
2014-02-06 14:49:23 +01:00
class ServerRankingPlugin implements Plugin , CallbackListener , CommandListener {
2014-02-06 14:35:54 +01:00
/**
* Constants
*/
2014-02-06 22:51:32 +01:00
const PLUGIN_ID = 11 ;
const PLUGIN_VERSION = 0.1 ;
const PLUGIN_NAME = 'ServerRankingPlugin' ;
const PLUGIN_AUTHOR = 'kremsy' ;
const TABLE_RANK = 'mc_rank' ;
const RANKING_TYPE_RECORDS = 'Records' ;
const RANKING_TYPE_RATIOS = 'Ratios' ;
const RANKING_TYPE_POINTS = 'Points' ;
const SETTING_MIN_RANKING_TYPE = 'ServerRankings Type Records/Points/Ratios' ;
const SETTING_MIN_HITS_RATIO_RANKING = 'Min Hits on Ratio Rankings' ;
const SETTING_MIN_HITS_POINTS_RANKING = 'Min Hits on Points Rankings' ;
const SETTING_MIN_REQUIRED_RECORDS = 'Minimum amount of records required on Records Ranking' ;
const SETTING_MAX_STORED_RECORDS = 'Maximum number of records per map for calculations' ;
const CB_RANK_BUILT = 'ServerRankingPlugin.RankBuilt' ;
2014-02-06 14:35:54 +01:00
/**
* Private Properties
*/
/** @var maniaControl $maniaControl * */
private $maniaControl = null ;
private $recordCount = 0 ;
/**
* Prepares the Plugin
*
* @ param ManiaControl $maniaControl
* @ return mixed
*/
public static function prepare ( ManiaControl $maniaControl ) {
//Todo
}
/**
* Load the plugin
*
* @ param \ManiaControl\ManiaControl $maniaControl
* @ return bool
*/
public function load ( ManiaControl $maniaControl ) {
$this -> maniaControl = $maniaControl ;
$this -> initTables ();
$maniaControl -> settingManager -> initSetting ( $this , self :: SETTING_MIN_HITS_RATIO_RANKING , 100 );
2014-02-06 22:51:32 +01:00
$maniaControl -> settingManager -> initSetting ( $this , self :: SETTING_MIN_HITS_POINTS_RANKING , 15 );
2014-02-06 14:35:54 +01:00
$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 ));
2014-02-06 21:18:25 +01:00
2014-02-06 14:35:54 +01:00
if ( $titlePrefix == 'tm' ) { //TODO also add obstacle here as default
$maniaControl -> settingManager -> initSetting ( $this , self :: SETTING_MIN_RANKING_TYPE , self :: RANKING_TYPE_RECORDS );
2014-02-06 21:18:25 +01:00
} else if ( $this -> maniaControl -> client -> getScriptName ()[ " CurrentValue " ] == " InstaDM.Script.txt " ) {
2014-02-06 14:35:54 +01:00
$maniaControl -> settingManager -> initSetting ( $this , self :: SETTING_MIN_RANKING_TYPE , self :: RANKING_TYPE_RATIOS );
} else {
2014-02-06 22:51:32 +01:00
$maniaControl -> settingManager -> initSetting ( $this , self :: SETTING_MIN_RANKING_TYPE , self :: RANKING_TYPE_POINTS );
2014-02-06 14:35:54 +01:00
}
//Check if the type is Correct
$type = $this -> maniaControl -> settingManager -> getSetting ( $this , self :: SETTING_MIN_RANKING_TYPE );
2014-02-06 22:51:32 +01:00
if ( $type != self :: RANKING_TYPE_RECORDS && $type != self :: RANKING_TYPE_POINTS && $type != self :: RANKING_TYPE_RATIOS ) {
$error = 'Ranking Type is not correct, possible values(' . self :: RANKING_TYPE_RATIOS . ', ' . self :: RANKING_TYPE_POINTS . ', ' . self :: RANKING_TYPE_POINTS . ')' ;
2014-02-06 14:35:54 +01:00
throw new Exception ( $error );
}
2014-02-06 14:49:23 +01:00
//Register CallbackListeners
$this -> maniaControl -> callbackManager -> registerCallbackListener ( PlayerManager :: CB_PLAYERJOINED , $this , 'handlePlayerConnect' );
$this -> maniaControl -> callbackManager -> registerCallbackListener ( CallbackManager :: CB_MC_ENDMAP , $this , 'handleEndMap' );
//Register CommandListener
$this -> maniaControl -> commandManager -> registerCommandListener ( 'rank' , $this , 'command_showRank' , false );
$this -> maniaControl -> commandManager -> registerCommandListener ( 'nextrank' , $this , 'command_nextRank' , false );
2014-02-06 21:18:25 +01:00
$this -> resetRanks (); //TODO only update records count
2014-02-06 14:35:54 +01:00
}
/**
* Unload the plugin and its resources
*/
public function unload () {
$this -> maniaControl -> callbackManager -> unregisterCallbackListener ( $this );
2014-02-06 14:49:23 +01:00
$this -> maniaControl -> commandManager -> unregisterCommandListener ( $this );
2014-02-06 14:35:54 +01:00
}
/**
* 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 () {
2014-02-06 22:51:32 +01:00
return " ServerRanking Plugin, ServerRanking by an avg build from the records, per count of points, or by a multiplication from Kill/Death Ratio and Laser accuracy " ;
2014-02-06 14:35:54 +01:00
}
/**
* 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 );
2014-02-06 14:49:23 +01:00
$hits = $this -> maniaControl -> statisticManager -> getStatsRanking ( StatisticCollector :: STAT_ON_HIT , - 1 , $minHits );
2014-02-06 14:35:54 +01:00
$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 ;
}
2014-02-06 21:18:25 +01:00
$ranks [ $player ] = $killDeathRatios [ $player ] * $accuracies [ $player ] * 1000 ;
2014-02-06 14:35:54 +01:00
}
arsort ( $ranks );
2014-02-06 21:18:25 +01:00
2014-02-06 14:35:54 +01:00
break ;
2014-02-06 22:51:32 +01:00
case self :: RANKING_TYPE_POINTS :
$minHits = $this -> maniaControl -> settingManager -> getSetting ( $this , self :: SETTING_MIN_HITS_POINTS_RANKING );
2014-02-06 14:35:54 +01:00
$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 );
2014-02-13 19:31:29 +01:00
//FIXME requiredrecords = NULL
$requiredRecords = 3 ;
2014-02-06 14:35:54 +01:00
$query = ' SELECT playerIndex , COUNT ( * ) AS Cnt
FROM ' . LocalRecordsPlugin::TABLE_RECORDS . '
2014-02-13 19:31:29 +01:00
GROUP BY PlayerIndex
2014-02-06 14:35:54 +01:00
HAVING Cnt >= ' . $requiredRecords ;
2014-02-13 19:31:29 +01:00
2014-02-06 14:35:54 +01:00
$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 ;
}
2014-02-06 14:49:23 +01:00
asort ( $ranks );
2014-02-06 14:35:54 +01:00
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 ) {
2014-02-06 21:18:25 +01:00
$player = $callback [ 1 ];
2014-02-06 14:35:54 +01:00
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 ) {
2014-02-09 09:50:07 +01:00
/** @var Player $player */
if ( $player -> isFakePlayer ()) {
continue ;
}
2014-02-06 14:35:54 +01:00
$this -> showRank ( $player );
$this -> showNextRank ( $player );
}
2014-02-06 14:49:23 +01:00
// Trigger callback
$this -> maniaControl -> callbackManager -> triggerCallback ( self :: CB_RANK_BUILT , array ( self :: CB_RANK_BUILT ));
2014-02-06 14:35:54 +01:00
}
/**
* 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 :
2014-02-06 21:18:25 +01:00
$kd = $this -> maniaControl -> statisticManager -> getStatisticData ( StatisticManager :: SPECIAL_STAT_KD_RATIO , $player -> index );
$acc = $this -> maniaControl -> statisticManager -> getStatisticData ( StatisticManager :: SPECIAL_STAT_LASER_ACC , $player -> index );
$message = '$0f3Your Server rank is $<$ff3' . $rankObj -> rank . '$> / $<$fff' . $this -> recordCount . '$> (K/D: $<$fff' . round ( $kd , 2 ) . '$> Acc: $<$fff' . round ( $acc * 100 ) . '%$>)' ;
2014-02-06 14:35:54 +01:00
break ;
2014-02-06 22:51:32 +01:00
case self :: RANKING_TYPE_POINTS :
$message = '$0f3Your Server rank is $<$ff3' . $rankObj -> rank . '$> / $<$fff' . $this -> recordCount . '$> Points: $fff' . $rankObj -> avg ;
2014-02-06 14:35:54 +01:00
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 );
2014-02-06 22:51:32 +01:00
$message = '$0f3 You must make $<$fff' . $minHits . '$> Hits on this server before receiving a rank...' ;
2014-02-06 14:35:54 +01:00
break ;
2014-02-06 22:51:32 +01:00
case self :: RANKING_TYPE_POINTS :
$minHits = $this -> maniaControl -> settingManager -> getSetting ( $this , self :: SETTING_MIN_HITS_POINTS_RANKING );
$message = '$0f3 You must make $<$fff' . $minHits . '$> Hits on this server before receiving a rank...' ;
2014-02-06 14:35:54 +01:00
break ;
case self :: RANKING_TYPE_RECORDS :
$minHits = $this -> maniaControl -> settingManager -> getSetting ( $this , self :: SETTING_MIN_REQUIRED_RECORDS );
2014-02-06 22:51:32 +01:00
$message = '$0f3 You need $<$fff' . $minHits . '$> Records on this server before receiving a rank...' ;
2014-02-06 14:35:54 +01:00
}
}
$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 ;
}
}
2014-02-06 14:49:23 +01:00
/**
* Shows the current Server - Rank
*
* @ param array $chatCallback
* @ param Player $player
*/
public function command_showRank ( array $chatCallback , Player $player ) {
$this -> showRank ( $player );
}
/**
* Show the next better ranked player
*
* @ param array $chatCallback
* @ param Player $player
*/
public function command_nextRank ( array $chatCallback , Player $player ) {
if ( ! $this -> showNextRank ( $player )) {
$message = '$0f3You need to have a ServerRank first!' ;
$this -> maniaControl -> chat -> sendChat ( $message , $player -> login );
}
}
2014-02-06 14:35:54 +01:00
/**
* Shows which Player is next ranked to you
*
* @ param Player $player
*/
public function showNextRank ( Player $player ) {
$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 );
2014-02-06 14:49:23 +01:00
return true ;
2014-02-06 14:35:54 +01:00
}
2014-02-06 14:49:23 +01:00
return false ;
2014-02-06 14:35:54 +01:00
}
}
/**
* Rank Structure
*/
class Rank extends AbstractStructure {
public $playerIndex ;
public $rank ;
public $avg ;
}