2023-09-08 17:39:56 +02:00
< ? php
namespace Beu ;
use FML\Controls\Frame ;
use FML\Controls\Quads\Quad_BgsPlayerCard ;
use FML\ManiaLink ;
use FML\Script\Features\Paging ;
use ManiaControl\ManiaControl ;
use ManiaControl\Plugins\Plugin ;
use ManiaControl\Players\PlayerManager ;
use ManiaControl\Callbacks\CallbackListener ;
use ManiaControl\Callbacks\Callbacks ;
2023-09-08 19:58:16 +02:00
use ManiaControl\Callbacks\Structures\TrackMania\OnWayPointEventStructure ;
2023-09-08 17:39:56 +02:00
use ManiaControl\Commands\CommandListener ;
use ManiaControl\Manialinks\LabelLine ;
use ManiaControl\Manialinks\ManialinkManager ;
use ManiaControl\Players\Player ;
use ManiaControl\Callbacks\TimerListener ;
use ManiaControl\Manialinks\ManialinkPageAnswerListener ;
use ManiaControl\Maps\Map ;
2023-09-08 19:58:16 +02:00
use ManiaControl\Utils\Formatter ;
2023-09-08 17:39:56 +02:00
/**
* ClimbTheMap
*
* @ author Beu
* @ license http :// www . gnu . org / licenses / GNU General Public License , Version 3
*/
class ClimbTheMap implements ManialinkPageAnswerListener , TimerListener , CommandListener , CallbackListener , Plugin {
/*
* Constants
*/
const PLUGIN_ID = 192 ;
2023-09-25 10:54:00 +02:00
const PLUGIN_VERSION = 1.1 ;
2023-09-08 17:39:56 +02:00
const PLUGIN_NAME = 'ClimbTheMap' ;
const PLUGIN_AUTHOR = 'Beu' ;
const DB_CLIMBTHEMAP = " ClimbTheMap " ;
const MLID_ALTITUDE_RECORDS = " ClimbTheMap.AltitudeRecords " ;
// Callbacks
const CB_UPDATEPBS = 'Trackmania.ClimbTheMap.UpdatePBs' ;
2023-09-08 19:58:41 +02:00
const CB_REQUESTPB = 'Trackmania.ClimbTheMap.RequestPB' ;
2023-09-08 17:39:56 +02:00
// Methods
const M_SETPLAYERSPB = 'Trackmania.ClimbTheMap.SetPlayersPB' ;
const M_SETWR = 'Trackmania.ClimbTheMap.SetWR' ;
// Actions
const A_SHOW_ALTITUDE_RECORDS = 'Trackmania.ClimbTheMap.ShowAltitudeRecords' ;
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null ;
private $manialink = " " ;
private $wraltitude = 0 ;
2023-09-08 19:58:16 +02:00
private $wrtime = 0 ;
2023-09-08 17:39:56 +02:00
/**
* @ see \ManiaControl\Plugins\Plugin :: prepare ()
*/
public static function prepare ( ManiaControl $maniaControl ) {
}
/**
* @ see \ManiaControl\Plugins\Plugin :: getId ()
*/
public static function getId () {
return self :: PLUGIN_ID ;
}
/**
* @ see \ManiaControl\Plugins\Plugin :: getName ()
*/
public static function getName () {
return self :: PLUGIN_NAME ;
}
/**
* @ see \ManiaControl\Plugins\Plugin :: getVersion ()
*/
public static function getVersion () {
return self :: PLUGIN_VERSION ;
}
/**
* @ see \ManiaControl\Plugins\Plugin :: getAuthor ()
*/
public static function getAuthor () {
return self :: PLUGIN_AUTHOR ;
}
/**
* @ see \ManiaControl\Plugins\Plugin :: getDescription ()
*/
public static function getDescription () {
2023-09-25 10:54:00 +02:00
return " [TM2020 only] Used to save the altitude records for the ClimbTheMap game mode " ;
2023-09-08 17:39:56 +02:00
}
/**
* @ see \ManiaControl\Plugins\Plugin :: load ()
*/
public function load ( ManiaControl $maniaControl ) {
$this -> maniaControl = $maniaControl ;
$this -> initTables ();
$this -> maniaControl -> getCallbackManager () -> registerCallbackListener ( Callbacks :: AFTERINIT , $this , 'handleAfterInit' );
$this -> maniaControl -> getCallbackManager () -> registerCallbackListener ( Callbacks :: MP_STARTROUNDSTART , $this , 'handleStartRound' );
2023-09-08 19:58:16 +02:00
$this -> maniaControl -> getCallbackManager () -> registerCallbackListener ( Callbacks :: TM_ONFINISHLINE , $this , 'handleFinishCallback' );
2023-09-08 17:39:56 +02:00
$this -> maniaControl -> getCallbackManager () -> registerScriptCallbackListener ( self :: CB_UPDATEPBS , $this , 'handleUpdatePBs' );
2023-09-08 19:58:41 +02:00
$this -> maniaControl -> getCallbackManager () -> registerScriptCallbackListener ( self :: CB_REQUESTPB , $this , 'handleRequestPB' );
2023-09-08 17:39:56 +02:00
$this -> maniaControl -> getManialinkManager () -> registerManialinkPageAnswerListener ( self :: A_SHOW_ALTITUDE_RECORDS , $this , 'handleShowAltitudeRecords' );
$this -> maniaControl -> getCommandManager () -> registerCommandListener ( 'records' , $this , 'handleShowAltitudeRecords' , false );
$this -> maniaControl -> getTimerManager () -> registerTimerListening ( $this , 'handle1Minute' , 60000 );
}
private function initTables () {
$mysqli = $this -> maniaControl -> getDatabase () -> getMysqli ();
$query = 'CREATE TABLE IF NOT EXISTS `' . self :: DB_CLIMBTHEMAP . ' ` (
`index` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
`mapIndex` INT ( 11 ) NOT NULL ,
`login` varchar ( 36 ) NOT NULL ,
`altitude` INT ( 11 ) NOT NULL ,
2023-09-08 19:58:16 +02:00
`time` int ( 11 ) DEFAULT - 1 ,
`date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,
2023-09-08 17:39:56 +02:00
PRIMARY KEY ( `index` ),
UNIQUE KEY `map_player` ( `mapIndex` , `login` )
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci ; ' ;
$mysqli -> query ( $query );
if ( $mysqli -> error ) {
trigger_error ( $mysqli -> error , E_USER_ERROR );
}
}
public function handleAfterInit () {
$this -> handleStartRound ();
}
public function handleStartRound () {
$map = $this -> maniaControl -> getMapManager () -> getCurrentMap ();
$logins = [];
foreach ( $this -> maniaControl -> getPlayerManager () -> getPlayers () as $player ) {
$logins [] = $player -> login ;
}
// Send PB
$pbs = $this -> getPlayersPB ( $map -> index , $logins );
if ( count ( $pbs ) > 0 ) {
$this -> maniaControl -> getClient () -> triggerModeScriptEvent ( self :: M_SETPLAYERSPB , [ json_encode ( $pbs )]);
}
// Send WR
$wr = $this -> getWR ( $map -> index );
if ( $wr !== null ) {
$this -> wraltitude = $wr [ 1 ];
2023-09-08 19:58:16 +02:00
$this -> wrtime = $wr [ 2 ];
$this -> maniaControl -> getClient () -> triggerModeScriptEvent ( self :: M_SETWR , [ $wr [ 0 ], strval ( $wr [ 1 ]), strval ( $wr [ 2 ])]);
2023-09-08 17:39:56 +02:00
} else {
$this -> wraltitude = 0 ;
}
}
2023-09-08 19:58:16 +02:00
public function handleFinishCallback ( OnWayPointEventStructure $structure ) {
$map = $this -> maniaControl -> getMapManager () -> getCurrentMap ();
if ( $map === null ) return ;
$mapIndex = $map -> index ;
$login = $structure -> getLogin ();
$time = $structure -> getRaceTime ();
$mysqli = $this -> maniaControl -> getDatabase () -> getMysqli ();
$stmt = $mysqli -> prepare ( " INSERT INTO ` " . self :: DB_CLIMBTHEMAP . " ` (`mapIndex`, `login`, `time`, `altitude`)
VALUES ( ? , ? , ? , - 1 ) ON DUPLICATE KEY UPDATE
`time` = IF ( `time` < 0 OR `time` > VALUES ( `time` ),
VALUES ( `time` ),
`time` ); " );
$stmt -> bind_param ( 'isi' , $mapIndex , $login , $time );
$stmt -> execute ();
// Reset manialink cache
$this -> manialink = " " ;
}
2023-09-08 17:39:56 +02:00
public function handleUpdatePBs ( array $data ) {
$json = json_decode ( $data [ 1 ][ 0 ]);
if ( $json !== null ) {
$map = $this -> maniaControl -> getMapManager () -> getCurrentMap ();
$mapIndex = - 1 ;
if ( $map !== null ) $mapIndex = $map -> index ;
$mysqli = $this -> maniaControl -> getDatabase () -> getMysqli ();
$mysqli -> begin_transaction ();
$stmt = $mysqli -> prepare ( " INSERT INTO ` " . self :: DB_CLIMBTHEMAP . " ` (`mapIndex`, `login`, `altitude`)
VALUES ( ? , ? , ? ) ON DUPLICATE KEY UPDATE
`altitude` = GREATEST ( VALUES ( `altitude` ), `altitude` ); " );
$stmt -> bind_param ( 'iss' , $mapIndex , $login , $altitude );
foreach ( $json as $login => $altitude ) {
$stmt -> execute ();
}
$mysqli -> commit ();
// Reset manialink cache
$this -> manialink = " " ;
}
}
2023-09-08 19:58:41 +02:00
/**
* Handle when a player connects
* Can 't use the C++ Callback because the it' s received by Maniacontrol before that the Maniascript initialized the Player
*
* @ param Player $player
*/
public function handleRequestPB ( array $data ) {
$login = $data [ 1 ][ 0 ];
$map = $this -> maniaControl -> getMapManager () -> getCurrentMap ();
if ( $map === null ) return ;
// Send PB
$pbs = $this -> getPlayersPB ( $map -> index , [ $login ]);
if ( count ( $pbs ) > 0 ) {
$this -> maniaControl -> getClient () -> triggerModeScriptEvent ( self :: M_SETPLAYERSPB , [ json_encode ( $pbs )]);
}
}
2023-09-08 17:39:56 +02:00
public function handle1Minute () {
$map = $this -> maniaControl -> getMapManager () -> getCurrentMap ();
if ( $map === null ) return ;
$wr = $this -> getWR ( $map -> index );
// Update WR if done on an another server
2023-09-08 19:58:16 +02:00
if ( $wr !== null && ( $this -> wraltitude !== $wr [ 1 ] || $this -> wrtime !== $wr [ 2 ])) {
2023-09-08 17:39:56 +02:00
$this -> wraltitude = $wr [ 1 ];
2023-09-08 19:58:16 +02:00
$this -> wrtime = $wr [ 2 ];
$this -> maniaControl -> getClient () -> triggerModeScriptEvent ( self :: M_SETWR , [ $wr [ 0 ], strval ( $wr [ 1 ]), strval ( $wr [ 2 ])]);
2023-09-25 10:54:00 +02:00
// Reset manialink cache
$this -> manialink = " " ;
2023-09-08 17:39:56 +02:00
}
}
private function getPlayersPB ( int $mapIndex , array $logins ) {
2023-09-08 19:58:16 +02:00
if ( count ( $logins ) === 0 ) return [];
2023-09-08 17:39:56 +02:00
$return = [];
$mysqli = $this -> maniaControl -> getDatabase () -> getMysqli ();
$stmt = $mysqli -> prepare ( 'SELECT login,altitude FROM `' . self :: DB_CLIMBTHEMAP . '` WHERE `mapIndex` = ? and login IN (' . str_repeat ( '?,' , count ( $logins ) - 1 ) . '?' . ' )' );
$stmt -> bind_param ( 'i' . str_repeat ( 's' , count ( $logins )), $mapIndex , ... $logins ); // bind array at once
if ( ! $stmt -> execute ()) {
trigger_error ( 'Error executing MySQL query: ' . $stmt -> error );
}
$result = $stmt -> get_result (); // get the mysqli result
if ( $result !== false ) {
foreach ( $result -> fetch_all ( MYSQLI_ASSOC ) as $data ) {
$return [ $data [ " login " ]] = $data [ " altitude " ];
}
}
return $return ;
}
private function getWR ( int $mapIndex ) {
$mysqli = $this -> maniaControl -> getDatabase () -> getMysqli ();
2023-09-08 19:58:16 +02:00
$stmt = $mysqli -> prepare ( 'SELECT `login`,`altitude`,`time` FROM `' . self :: DB_CLIMBTHEMAP . ' `
WHERE `mapIndex` = ?
ORDER BY
CASE
WHEN `time` > 0 THEN `time`
ELSE `altitude`
END DESC ,
`date` ASC
LIMIT 1 ; ' );
2023-09-08 17:39:56 +02:00
$stmt -> bind_param ( 'i' , $mapIndex );
if ( ! $stmt -> execute ()) {
trigger_error ( 'Error executing MySQL query: ' . $stmt -> error );
}
$result = $stmt -> get_result ();
if ( $result !== false ) {
$data = $result -> fetch_assoc ();
if ( $data !== null ) {
$player = $this -> maniaControl -> getPlayerManager () -> getPlayer ( $data [ " login " ]);
if ( $player !== null ) {
2023-09-08 19:58:16 +02:00
return [ $player -> nickname , $data [ " altitude " ], $data [ " time " ]];
2023-09-08 17:39:56 +02:00
}
}
}
return null ;
}
public function getRecords ( Map $map ) {
if ( $map === null ) return [];
$mapIndex = $map -> index ;
$mysqli = $this -> maniaControl -> getDatabase () -> getMysqli ();
2023-09-08 19:58:16 +02:00
$stmt = $mysqli -> prepare ( 'SELECT ctm.index,ctm.login,p.nickname,ctm.altitude,ctm.time,ctm.date FROM `' . self :: DB_CLIMBTHEMAP . ' ` ctm
2023-09-08 17:39:56 +02:00
LEFT JOIN `' . PlayerManager::TABLE_PLAYERS . '` p
ON ctm . login = p . login
WHERE `mapIndex` = ?
2023-09-08 19:58:16 +02:00
ORDER BY
CASE
WHEN `time` > 0 THEN `time`
ELSE `altitude`
END DESC ,
`date` ASC ' );
2023-09-08 17:39:56 +02:00
$stmt -> bind_param ( 'i' , $mapIndex );
if ( ! $stmt -> execute ()) {
trigger_error ( 'Error executing MySQL query: ' . $stmt -> error );
}
$result = $stmt -> get_result (); // get the mysqli result
if ( $result !== false ) {
return $result -> fetch_all ( MYSQLI_ASSOC );
}
return [];
}
public function handleShowAltitudeRecords ( array $callback , Player $player ) {
$this -> maniaControl -> getManialinkManager () -> displayWidget ( $this -> getManialink (), $player , self :: MLID_ALTITUDE_RECORDS );
}
private function getManialink () {
if ( $this -> manialink !== " " ) return $this -> manialink ;
$width = $this -> maniaControl -> getManialinkManager () -> getStyleManager () -> getListWidgetsWidth ();
$height = $this -> maniaControl -> getManialinkManager () -> getStyleManager () -> getListWidgetsHeight ();
// get PlayerList
$records = $this -> getRecords ( $this -> maniaControl -> getMapManager () -> getCurrentMap ());
// create manialink
$maniaLink = new ManiaLink ( ManialinkManager :: MAIN_MLID );
$script = $maniaLink -> getScript ();
$paging = new Paging ();
$script -> addFeature ( $paging );
// Main frame
$frame = $this -> maniaControl -> getManialinkManager () -> getStyleManager () -> getDefaultListFrame ( $script , $paging );
$maniaLink -> addChild ( $frame );
// Start offsets
$posX = - $width / 2 ;
$posY = $height / 2 ;
// Headline
$headFrame = new Frame ();
$frame -> addChild ( $headFrame );
$headFrame -> setY ( $posY - 5 );
$labelLine = new LabelLine ( $headFrame );
$labelLine -> addLabelEntryText ( 'Rank' , $posX + 5 );
$labelLine -> addLabelEntryText ( 'Nickname' , $posX + 18 );
2023-09-08 19:58:16 +02:00
$labelLine -> addLabelEntryText ( 'Altitude' , $posX + $width * 0.5 );
$labelLine -> addLabelEntryText ( 'Time' , $posX + $width * 0.6 );
2023-09-08 17:39:56 +02:00
$labelLine -> addLabelEntryText ( 'Date (UTC)' , $posX + $width * 0.75 );
$labelLine -> render ();
$index = 0 ;
$posY = $height / 2 - 10 ;
$pageFrame = null ;
$pageMaxCount = floor (( $height - 5 - 10 ) / 4 );
foreach ( $records as $record ) {
if ( $index % $pageMaxCount === 0 ) {
$pageFrame = new Frame ();
$frame -> addChild ( $pageFrame );
$posY = $height / 2 - 10 ;
$paging -> addPageControl ( $pageFrame );
}
$recordFrame = new Frame ();
$pageFrame -> addChild ( $recordFrame );
if ( $index % 2 === 0 ) {
$lineQuad = new Quad_BgsPlayerCard ();
$recordFrame -> addChild ( $lineQuad );
$lineQuad -> setSize ( $width , 4 );
$lineQuad -> setSubStyle ( $lineQuad :: SUBSTYLE_BgPlayerCardBig );
$lineQuad -> setZ ( - 0.001 );
}
$labelLine = new LabelLine ( $recordFrame );
$labelLine -> addLabelEntryText ( $index + 1 , $posX + 5 , 13 );
$labelLine -> addLabelEntryText ( $record [ " nickname " ], $posX + 18 , 52 );
2023-09-08 19:58:16 +02:00
$labelLine -> addLabelEntryText ( $record [ " altitude " ], $posX + $width * 0.5 , 31 );
if ( $record [ " time " ] > 0 ) {
$labelLine -> addLabelEntryText ( Formatter :: formatTime ( $record [ " time " ]), $posX + $width * 0.6 , 30 );
}
$labelLine -> addLabelEntryText ( $record [ " date " ], $posX + $width * 0.75 , 30 );
2023-09-08 17:39:56 +02:00
$labelLine -> render ();
$recordFrame -> setY ( $posY );
$posY -= 4 ;
$index ++ ;
}
$this -> manialink = ( string ) $maniaLink ;
return $this -> manialink ;
}
/**
* Unload the plugin and its Resources
*/
public function unload () {
}
}