ManiacontrolPlugins/Beu/ClimbTheMap.php

421 lines
13 KiB
PHP
Raw Permalink Normal View History

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;
const PLUGIN_VERSION = 1.4;
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` = ?
2023-09-27 11:02:26 +02:00
ORDER BY (CASE WHEN `time` > 0 THEN `time` ELSE 9999999999 END) ASC,
`altitude` DESC,
2023-09-08 19:58:16 +02:00
`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-26 19:00:54 +02:00
$query = 'SELECT ctm.index,ctm.login,p.nickname,ctm.altitude,ctm.time,ctm.date FROM `' . self::DB_CLIMBTHEMAP . '` ctm
LEFT JOIN `' . PlayerManager::TABLE_PLAYERS . '` p
ON ctm.login = p.login
WHERE `mapIndex` = ?
ORDER BY (CASE WHEN `time` > 0 THEN `time` ELSE 9999999999 END) ASC,
`altitude` DESC,
`date` ASC
LIMIT 500';
2023-09-26 19:00:54 +02:00
$stmt = $mysqli->prepare($query );
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() {
}
}