958 lines
32 KiB
PHP
958 lines
32 KiB
PHP
<?php
|
|
|
|
namespace MCTeam;
|
|
|
|
use FML\Controls\Frame;
|
|
use FML\Controls\Label;
|
|
use FML\Controls\Quad;
|
|
use FML\Controls\Quads\Quad_BgsPlayerCard;
|
|
use FML\Controls\Quads\Quad_Icons64x64_1;
|
|
use FML\ManiaLink;
|
|
use FML\Script\Features\Paging;
|
|
use ManiaControl\Admin\AuthenticationManager;
|
|
use ManiaControl\Callbacks\CallbackListener;
|
|
use ManiaControl\Callbacks\Callbacks;
|
|
use ManiaControl\Callbacks\CallQueueListener;
|
|
use ManiaControl\Callbacks\Structures\TrackMania\OnWayPointEventStructure;
|
|
use ManiaControl\Callbacks\TimerListener;
|
|
use ManiaControl\Commands\CommandListener;
|
|
use ManiaControl\Logger;
|
|
use ManiaControl\ManiaControl;
|
|
use ManiaControl\Manialinks\LabelLine;
|
|
use ManiaControl\Manialinks\ManialinkManager;
|
|
use ManiaControl\Manialinks\ManialinkPageAnswerListener;
|
|
use ManiaControl\Maps\Map;
|
|
use ManiaControl\Players\Player;
|
|
use ManiaControl\Players\PlayerManager;
|
|
use ManiaControl\Plugins\Plugin;
|
|
use ManiaControl\Settings\Setting;
|
|
use ManiaControl\Settings\SettingManager;
|
|
use ManiaControl\Utils\DataUtil;
|
|
use ManiaControl\Utils\Formatter;
|
|
use MCTeam\Common\RecordWidget;
|
|
|
|
/**
|
|
* ManiaControl Local Records Plugin
|
|
*
|
|
* @author ManiaControl Team <mail@maniacontrol.com>
|
|
* @copyright 2014-2020 ManiaControl Team
|
|
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
|
|
*/
|
|
class LocalRecordsPlugin implements CallbackListener, CallQueueListener, CommandListener, ManialinkPageAnswerListener, TimerListener, Plugin {
|
|
/*
|
|
* Constants
|
|
*/
|
|
const ID = 7;
|
|
const VERSION = 0.8;
|
|
const NAME = 'Local Records Plugin';
|
|
const AUTHOR = 'MCTeam';
|
|
const MLID_RECORDS = 'ml_local_records';
|
|
const TABLE_RECORDS = 'mc_localrecords';
|
|
const PERMISSION_DELETE_ANY_RECORD = 'Permission remove any record';
|
|
const PERMISSION_DELETE_PERSONAL_RECORD = 'Permission remove personal record';
|
|
const SETTING_MULTILAP_SAVE_SINGLE = 'Save every Lap as Record in Multilap';
|
|
const SETTING_WIDGET_TITLE = 'Widget Title';
|
|
const SETTING_WIDGET_POSX = 'Widget Position: X';
|
|
const SETTING_WIDGET_POSY = 'Widget Position: Y';
|
|
const SETTING_WIDGET_WIDTH = 'Widget Width';
|
|
const SETTING_WIDGET_LINESCOUNT = 'Widget Displayed Lines Count';
|
|
const SETTING_WIDGET_LINEHEIGHT = 'Widget Line Height';
|
|
const SETTING_WIDGET_ENABLE = 'Enable Local Records Widget';
|
|
const SETTING_NOTIFICATION_MESSAGE_PREFIX = 'Notification Message Prefix';
|
|
const SETTING_NOTIFY_BEST_RECORDS_PRIVATE = 'Notify privately for the X Best Records';
|
|
const SETTING_NOTIFY_BEST_RECORDS_PUBLIC = 'Notify publicly for the X Best Records';
|
|
const SETTING_ADJUST_OUTER_BORDER = 'Adjust outer Border to Number of actual Records';
|
|
const SETTING_RECORDS_BEFORE_AFTER = 'Number of Records displayed before and after a player';
|
|
const CB_LOCALRECORDS_CHANGED = 'LocalRecords.Changed';
|
|
const ACTION_SHOW_RECORDSLIST = 'LocalRecords.ShowRecordsList';
|
|
const CSV_SPLITTER = ';';
|
|
|
|
|
|
/*
|
|
* Private properties
|
|
*/
|
|
/** @var ManiaControl $maniaControl */
|
|
private $maniaControl = null;
|
|
/** @var \MCTeam\Common\RecordWidget $recordWidget */
|
|
private $recordWidget = null;
|
|
private $updateManialink = false;
|
|
private $checkpoints = array();
|
|
private $scriptName = 'TimeAttack';
|
|
|
|
/**
|
|
* @see \ManiaControl\Plugins\Plugin::prepare()
|
|
*/
|
|
public static function prepare(ManiaControl $maniaControl) {
|
|
}
|
|
|
|
/**
|
|
* @see \ManiaControl\Plugins\Plugin::getId()
|
|
*/
|
|
public static function getId() {
|
|
return self::ID;
|
|
}
|
|
|
|
/**
|
|
* @see \ManiaControl\Plugins\Plugin::getName()
|
|
*/
|
|
public static function getName() {
|
|
return self::NAME;
|
|
}
|
|
|
|
/**
|
|
* @see \ManiaControl\Plugins\Plugin::getVersion()
|
|
*/
|
|
public static function getVersion() {
|
|
return self::VERSION;
|
|
}
|
|
|
|
/**
|
|
* @see \ManiaControl\Plugins\Plugin::getAuthor()
|
|
*/
|
|
public static function getAuthor() {
|
|
return self::AUTHOR;
|
|
}
|
|
|
|
/**
|
|
* @see \ManiaControl\Plugins\Plugin::getDescription()
|
|
*/
|
|
public static function getDescription() {
|
|
return 'Plugin offering tracking of local records and manialinks to display them.';
|
|
}
|
|
|
|
/**
|
|
* @see \ManiaControl\Plugins\Plugin::load()
|
|
* @param \ManiaControl\ManiaControl $maniaControl
|
|
* @return bool
|
|
*/
|
|
public function load(ManiaControl $maniaControl) {
|
|
$this->maniaControl = $maniaControl;
|
|
$this->initTables();
|
|
|
|
$this->recordWidget = new RecordWidget($this->maniaControl);
|
|
|
|
// Settings
|
|
$this->maniaControl->getAuthenticationManager()->definePluginPermissionLevel(
|
|
$this,
|
|
self::PERMISSION_DELETE_ANY_RECORD,
|
|
AuthenticationManager::AUTH_LEVEL_SUPERADMIN,
|
|
AuthenticationManager::AUTH_LEVEL_MODERATOR
|
|
);
|
|
$this->maniaControl->getAuthenticationManager()->definePluginPermissionLevel(
|
|
$this,
|
|
self::PERMISSION_DELETE_PERSONAL_RECORD,
|
|
AuthenticationManager::AUTH_LEVEL_ADMIN,
|
|
AuthenticationManager::AUTH_LEVEL_PLAYER
|
|
);
|
|
|
|
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_WIDGET_TITLE, 'Local Records');
|
|
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_WIDGET_POSX, -139.);
|
|
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_WIDGET_POSY, 75);
|
|
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_WIDGET_WIDTH, 40.);
|
|
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_WIDGET_LINESCOUNT, 15);
|
|
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_WIDGET_LINEHEIGHT, 4.);
|
|
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_WIDGET_ENABLE, true);
|
|
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_NOTIFICATION_MESSAGE_PREFIX, '$3c0');
|
|
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_NOTIFY_BEST_RECORDS_PRIVATE, 100);
|
|
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_NOTIFY_BEST_RECORDS_PUBLIC, 10);
|
|
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_ADJUST_OUTER_BORDER, false);
|
|
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_RECORDS_BEFORE_AFTER, 2);
|
|
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_MULTILAP_SAVE_SINGLE, false);
|
|
|
|
// Callbacks
|
|
$this->maniaControl->getTimerManager()->registerTimerListening($this, 'handle1Second', 1000);
|
|
$this->maniaControl->getTimerManager()->registerTimerListening($this, 'handle1Minute', 60000);
|
|
|
|
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::AFTERINIT, $this, 'handleAfterInit');
|
|
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::BEGINMAP, $this, 'handleMapBegin');
|
|
$this->maniaControl->getCallbackManager()->registerCallbackListener(SettingManager::CB_SETTING_CHANGED, $this, 'handleSettingChanged');
|
|
|
|
$this->maniaControl->getCallbackManager()->registerCallbackListener(PlayerManager::CB_PLAYERCONNECT, $this, 'handlePlayerConnect');
|
|
|
|
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::TM_ONWAYPOINT, $this, 'handleCheckpointCallback');
|
|
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::TM_ONFINISHLINE, $this, 'handleFinishCallback');
|
|
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::TM_ONLAPFINISH, $this, 'handleFinishLapCallback');
|
|
|
|
$this->maniaControl->getCommandManager()->registerCommandListener(array('exportrecs', 'exportrecords', 'recexport'), $this, 'exportRecordsList', true, 'Exports the records of a map to a CSV-file.');
|
|
$this->maniaControl->getCommandManager()->registerCommandListener(array('recs', 'records'), $this, 'showRecordsList', false, 'Shows a list of Local Records on the current map.');
|
|
$this->maniaControl->getCommandManager()->registerCommandListener('delrec', $this, 'deletePersonalRecord', false, 'Removes your record from the database.');
|
|
$this->maniaControl->getCommandManager()->registerCommandListener('delrec', $this, 'deleteAnyRecord', true, 'Removes any record from the database.');
|
|
|
|
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ACTION_SHOW_RECORDSLIST, $this, 'handleShowRecordsList');
|
|
|
|
$this->updateManialink = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Initialize needed database tables
|
|
*/
|
|
private function initTables() {
|
|
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
|
|
$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;";
|
|
$mysqli->query($query);
|
|
if ($mysqli->error) {
|
|
trigger_error($mysqli->error, E_USER_ERROR);
|
|
}
|
|
|
|
$mysqli->query("ALTER TABLE `" . self::TABLE_RECORDS . "` ADD `checkpoints` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL");
|
|
if ($mysqli->error) {
|
|
if ($mysqli->errno !== 1062) {
|
|
// Duplicate
|
|
trigger_error($mysqli->error, E_USER_ERROR);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see \ManiaControl\Plugins\Plugin::unload()
|
|
*/
|
|
public function unload() {
|
|
$this->maniaControl->getManialinkManager()->hideManialink(self::MLID_RECORDS);
|
|
}
|
|
|
|
/**
|
|
* Handle ManiaControl After Init
|
|
*
|
|
* @internal
|
|
*/
|
|
public function handleAfterInit() {
|
|
$this->updateManialink = true;
|
|
}
|
|
|
|
/**
|
|
* Handle 1 Second Callback
|
|
*
|
|
* @internal
|
|
*/
|
|
public function handle1Second() {
|
|
if (!$this->updateManialink) {
|
|
return;
|
|
}
|
|
|
|
$this->updateManialink = false;
|
|
if ($this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_WIDGET_ENABLE)) {
|
|
$this->sendWidgetManiaLink();
|
|
}
|
|
}
|
|
|
|
/** Fetch the Current Scriptname every Minute
|
|
*
|
|
* @internal
|
|
*/
|
|
public function handle1Minute() {
|
|
$scriptNameResponse = $this->maniaControl->getClient()->getScriptName();
|
|
$this->scriptName = str_replace('.Script.txt', '', $scriptNameResponse['CurrentValue']);
|
|
}
|
|
|
|
/**
|
|
* Build the local records widget ManiaLink and send it to the players
|
|
*
|
|
* @return string
|
|
*/
|
|
private function sendWidgetManiaLink() {
|
|
$map = $this->maniaControl->getMapManager()->getCurrentMap();
|
|
if (!$map) {
|
|
return null;
|
|
}
|
|
|
|
$title = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_WIDGET_TITLE);
|
|
$posX = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_WIDGET_POSX);
|
|
$posY = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_WIDGET_POSY);
|
|
$width = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_WIDGET_WIDTH);
|
|
$lines = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_WIDGET_LINESCOUNT);
|
|
$lineHeight = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_WIDGET_LINEHEIGHT);
|
|
$recordsBeforeAfter = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_RECORDS_BEFORE_AFTER);
|
|
$labelStyle = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultLabelStyle();
|
|
$quadStyle = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultQuadStyle();
|
|
$quadSubstyle = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultQuadSubstyle();
|
|
|
|
$records = $this->getLocalRecords($map, 1000); //TODO limit setting
|
|
if (!is_array($records)) {
|
|
Logger::logError("Couldn't fetch player records.");
|
|
return null;
|
|
}
|
|
|
|
//TODO maybe only store if player is connected
|
|
$playerRecords = array();
|
|
foreach ($records as $index => $record) {
|
|
$playerRecords[$record->playerIndex] = $index;
|
|
}
|
|
|
|
$frame = new Frame();
|
|
$frame->setPosition($posX, $posY);
|
|
|
|
$backgroundQuad = new Quad();
|
|
$frame->addChild($backgroundQuad);
|
|
$backgroundQuad->setVerticalAlign($backgroundQuad::TOP);
|
|
$adjustOuterBorder = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_ADJUST_OUTER_BORDER);
|
|
$height = 7. + ($adjustOuterBorder ? count($records) : $lines) * $lineHeight;
|
|
$backgroundQuad->setSize($width * 1.05, $height);
|
|
$backgroundQuad->setStyles($quadStyle, $quadSubstyle);
|
|
$backgroundQuad->setAction(self::ACTION_SHOW_RECORDSLIST);
|
|
|
|
$titleLabel = new Label();
|
|
$frame->addChild($titleLabel);
|
|
$titleLabel->setPosition(0, $lineHeight * -0.9);
|
|
$titleLabel->setWidth($width);
|
|
$titleLabel->setStyle($labelStyle);
|
|
$titleLabel->setTextSize(2);
|
|
$titleLabel->setText($title);
|
|
$titleLabel->setTranslate(true);
|
|
|
|
$topRecordsCount = $lines - $recordsBeforeAfter * 2 - 1;
|
|
$preGeneratedTopRecordsFrame = $this->recordWidget->generateRecordsFrame($records, $topRecordsCount);
|
|
$preGeneratedRecordsFrameWithRecord = $this->recordWidget->generateRecordsFrame($records, $lines - 1);
|
|
|
|
$players = $this->maniaControl->getPlayerManager()->getPlayers();
|
|
$playersWithoutRecord = array();
|
|
|
|
foreach ($players as $player) {
|
|
$sendManiaLink = true;
|
|
|
|
$maniaLink = new ManiaLink(self::MLID_RECORDS);
|
|
$maniaLink->addChild($frame);
|
|
|
|
$listFrame = new Frame();
|
|
$maniaLink->addChild($listFrame);
|
|
$listFrame->setPosition($posX, $posY);
|
|
|
|
if (isset($playerRecords[$player->index]) && $playerRecords[$player->index] >= $topRecordsCount) {
|
|
$listFrame->addChild($preGeneratedTopRecordsFrame);
|
|
|
|
$y = -8 - $topRecordsCount * $lineHeight;
|
|
$playerIndex = $playerRecords[$player->index];
|
|
|
|
//Line separator
|
|
$quad = $this->recordWidget->getLineSeparatorQuad($width);
|
|
$listFrame->addChild($quad);
|
|
$quad->setY($y + $lineHeight / 2);
|
|
|
|
//Generate the Records around a player and display below topRecords
|
|
for ($i = $playerIndex - $recordsBeforeAfter; $i <= $playerIndex + $recordsBeforeAfter; $i++) {
|
|
if (array_key_exists($i, $records)) { //If there are no records behind you
|
|
$recordFrame = $this->recordWidget->generateRecordLineFrame($records[$i], $player);
|
|
$recordFrame->setY($y);
|
|
$listFrame->addChild($recordFrame);
|
|
$y -= $lineHeight;
|
|
}
|
|
}
|
|
|
|
} else if (isset($playerRecords[$player->index]) && $playerRecords[$player->index] < $topRecordsCount) {
|
|
$playersWithoutRecord[] = $player;
|
|
$sendManiaLink = false;
|
|
} else {
|
|
if ($record = $this->getLocalRecord($map, $player)) {
|
|
$record->nickname = $player->nickname;
|
|
$recordFrame = $preGeneratedRecordsFrameWithRecord;
|
|
$listFrame->addChild($recordFrame);
|
|
|
|
$y = -8 - ($lines - 1) * $lineHeight;
|
|
|
|
//Line separator
|
|
$quad = $this->recordWidget->getLineSeparatorQuad($width);
|
|
$listFrame->addChild($quad);
|
|
$quad->setY($y + $lineHeight / 2);
|
|
|
|
$recordFrame = $this->recordWidget->generateRecordLineFrame($record, $player);
|
|
$listFrame->addChild($recordFrame);
|
|
$recordFrame->setY($y);
|
|
|
|
} else {
|
|
$playersWithoutRecord[] = $player;
|
|
$sendManiaLink = false;
|
|
}
|
|
}
|
|
|
|
if ($sendManiaLink) {
|
|
$this->maniaControl->getManialinkManager()->sendManialink($maniaLink, $player);
|
|
}
|
|
}
|
|
|
|
if ($playersWithoutRecord) {
|
|
$maniaLink = new ManiaLink(self::MLID_RECORDS);
|
|
$maniaLink->addChild($frame);
|
|
|
|
$listFrame = $this->recordWidget->generateRecordsFrame($records, $lines);
|
|
$maniaLink->addChild($listFrame);
|
|
$listFrame->setPosition($posX, $posY);
|
|
|
|
$this->maniaControl->getManialinkManager()->sendManialink($maniaLink, $playersWithoutRecord);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetch local records for the given map
|
|
*
|
|
* @api
|
|
* @param \ManiaControl\Maps\Map $map
|
|
* @param int $limit
|
|
* @return array|null
|
|
*/
|
|
public function getLocalRecords(Map $map, $limit = -1) {
|
|
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
|
|
$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;
|
|
}
|
|
|
|
/**
|
|
* Handle Setting Changed Callback
|
|
*
|
|
* @internal
|
|
* @param Setting $setting
|
|
*/
|
|
public function handleSettingChanged(Setting $setting) {
|
|
if (!$setting->belongsToClass($this)) {
|
|
return;
|
|
}
|
|
|
|
switch ($setting->setting) {
|
|
case self::SETTING_WIDGET_ENABLE: {
|
|
if ($setting->value) {
|
|
$this->updateManialink = true;
|
|
} else {
|
|
$this->maniaControl->getManialinkManager()->hideManialink(self::MLID_RECORDS);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
$this->updateRecordWidget();
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle Checkpoint Callback
|
|
*
|
|
* @internal
|
|
* @param \ManiaControl\Callbacks\Structures\TrackMania\OnWayPointEventStructure $structure
|
|
*/
|
|
public function handleCheckpointCallback(OnWayPointEventStructure $structure) {
|
|
$playerLogin = $structure->getLogin();
|
|
if (!isset($this->checkpoints[$playerLogin])) {
|
|
$this->checkpoints[$playerLogin] = array();
|
|
}
|
|
$this->checkpoints[$playerLogin][$structure->getCheckPointInLap()] = $structure->getLapTime();
|
|
}
|
|
|
|
|
|
/**
|
|
* Handle End of Lap Callback
|
|
*
|
|
* @internal
|
|
* @param \ManiaControl\Callbacks\Structures\TrackMania\OnWayPointEventStructure $structure
|
|
*/
|
|
public function handleFinishLapCallback(OnWayPointEventStructure $structure) {
|
|
|
|
$multiLapSaveSingle = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_MULTILAP_SAVE_SINGLE);
|
|
|
|
if ($this->scriptName != "TimeAttack" && !$multiLapSaveSingle) {
|
|
//Do Nothing on Finishing a Single Lap
|
|
} else {
|
|
//Save on every pass through of a Lap
|
|
$this->saveRecord($structure, $structure->getLapTime());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle Finish Callback
|
|
*
|
|
* @internal
|
|
* @param \ManiaControl\Callbacks\Structures\TrackMania\OnWayPointEventStructure $structure
|
|
*/
|
|
public function handleFinishCallback(OnWayPointEventStructure $structure) {
|
|
$multiLapSaveSingle = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_MULTILAP_SAVE_SINGLE);
|
|
|
|
if ($this->scriptName != "TimeAttack" && $multiLapSaveSingle) {
|
|
//Save last lap time only
|
|
$this->saveRecord($structure, $structure->getLapTime());
|
|
} else {
|
|
//Save full race time
|
|
$this->saveRecord($structure, $structure->getRaceTime());
|
|
}
|
|
}
|
|
|
|
|
|
private function saveRecord(OnWayPointEventStructure $structure, $time) {
|
|
if ($time <= 0) {
|
|
// Invalid time
|
|
return;
|
|
}
|
|
|
|
$map = $this->maniaControl->getMapManager()->getCurrentMap();
|
|
|
|
$player = $structure->getPlayer();
|
|
|
|
if (!$player) { //TODO verify why this can happen
|
|
return;
|
|
}
|
|
|
|
$checkpointsString = $this->getCheckpoints($player->login);
|
|
$this->checkpoints[$player->login] = array();
|
|
|
|
$message = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_NOTIFICATION_MESSAGE_PREFIX);
|
|
$notifyPrivatelyAt = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_NOTIFY_BEST_RECORDS_PRIVATE);
|
|
$notifyPubliclyAt = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_NOTIFY_BEST_RECORDS_PUBLIC);
|
|
|
|
// Check old record of the player
|
|
$oldRecord = $this->getLocalRecord($map, $player);
|
|
if ($oldRecord) {
|
|
if ($oldRecord->time < $time) {
|
|
// Not improved
|
|
return;
|
|
} else if ($oldRecord->time == $time) {
|
|
// Same time
|
|
if ($notifyPubliclyAt < $oldRecord->rank && $oldRecord->rank <= $notifyPrivatelyAt) {
|
|
$message .= 'You';
|
|
} else {
|
|
$message .= '$<$fff' . $player->nickname . '$>';
|
|
}
|
|
$message .= ' equalized the $<$ff0' . $oldRecord->rank . '.$> Local Record:';
|
|
$message .= ' $<$fff' . Formatter::formatTime($oldRecord->time) . '$>!';
|
|
|
|
if ($oldRecord->rank <= $notifyPubliclyAt) {
|
|
$this->maniaControl->getCallQueueManager()->registerListening(
|
|
$this,
|
|
function () use ($message) {
|
|
$this->maniaControl->getChat()->sendInformation($message);
|
|
}
|
|
);
|
|
} else if ($oldRecord->rank <= $notifyPrivatelyAt) {
|
|
$this->maniaControl->getCallQueueManager()->registerListening(
|
|
$this,
|
|
function () use ($message, $player) {
|
|
$this->maniaControl->getChat()->sendInformation($message, $player);
|
|
}
|
|
);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Save time
|
|
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
|
|
$query = "INSERT INTO `" . self::TABLE_RECORDS . "` (
|
|
`mapIndex`,
|
|
`playerIndex`,
|
|
`time`,
|
|
`checkpoints`
|
|
) VALUES (
|
|
{$map->index},
|
|
{$player->index},
|
|
{$time},
|
|
'{$checkpointsString}'
|
|
) ON DUPLICATE KEY UPDATE
|
|
`time` = VALUES(`time`),
|
|
`checkpoints` = VALUES(`checkpoints`);";
|
|
$mysqli->query($query);
|
|
if ($mysqli->error) {
|
|
trigger_error($mysqli->error);
|
|
return;
|
|
}
|
|
$this->updateManialink = true;
|
|
|
|
// Announce record
|
|
$newRecord = $this->getLocalRecord($map, $player);
|
|
$improvedRank = ($oldRecord && $newRecord->rank >= $oldRecord->rank);
|
|
|
|
if ($notifyPubliclyAt < $newRecord->rank && $newRecord->rank <= $notifyPrivatelyAt) {
|
|
$message .= 'You';
|
|
} else {
|
|
$message .= '$<$fff' . $player->nickname . '$>';
|
|
}
|
|
$message .= ' ' . ($improvedRank ? 'improved' : 'gained') . ' the';
|
|
$message .= ' $<$ff0' . $newRecord->rank . '.$> Local Record:';
|
|
$message .= ' $<$fff' . Formatter::formatTime($newRecord->time) . '$>!';
|
|
if ($oldRecord) {
|
|
$message .= ' (';
|
|
if ($improvedRank) {
|
|
$message .= '$<$ff0' . $oldRecord->rank . '.$> ';
|
|
}
|
|
$timeDiff = $oldRecord->time - $newRecord->time;
|
|
$message .= '$<$fff-' . Formatter::formatTime($timeDiff) . '$>)';
|
|
}
|
|
|
|
if ($newRecord->rank <= $notifyPubliclyAt) {
|
|
$this->maniaControl->getCallQueueManager()->registerListening(
|
|
$this,
|
|
function () use ($message) {
|
|
$this->maniaControl->getChat()->sendInformation($message);
|
|
}
|
|
);
|
|
} else if ($newRecord->rank <= $notifyPrivatelyAt) {
|
|
$this->maniaControl->getCallQueueManager()->registerListening(
|
|
$this,
|
|
function () use ($message, $player) {
|
|
$this->maniaControl->getChat()->sendInformation($message, $player);
|
|
}
|
|
);
|
|
}
|
|
|
|
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_LOCALRECORDS_CHANGED, $newRecord);
|
|
}
|
|
|
|
|
|
/**
|
|
* Get current checkpoint string for local record
|
|
*
|
|
* @api
|
|
* @param string $login
|
|
* @return string
|
|
*/
|
|
public function getCheckpoints($login) {
|
|
if (!$login || !isset($this->checkpoints[$login])) {
|
|
return null;
|
|
}
|
|
$string = '';
|
|
$count = count($this->checkpoints[$login]);
|
|
foreach ($this->checkpoints[$login] as $index => $check) {
|
|
$string .= $check;
|
|
if ($index < $count - 1) {
|
|
$string .= ',';
|
|
}
|
|
}
|
|
return $string;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the local record for the given map and login
|
|
*
|
|
* @api
|
|
* @param Map $map
|
|
* @param Player $player
|
|
* @return mixed
|
|
*/
|
|
public function getLocalRecord(Map $map, Player $player) {
|
|
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
|
|
$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;
|
|
}
|
|
|
|
/**
|
|
* Handle Player Connect Callback
|
|
*
|
|
* @internal
|
|
*/
|
|
public function handlePlayerConnect() {
|
|
$this->updateManialink = true;
|
|
}
|
|
|
|
/**
|
|
* Handle Begin Map Callback
|
|
*
|
|
* @internal
|
|
*/
|
|
public function handleMapBegin() {
|
|
$this->updateManialink = true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Handle the ManiaLink answer of the showRecordsList action
|
|
*
|
|
* @internal
|
|
* @param array $callback
|
|
* @param \ManiaControl\Players\Player $player
|
|
*/
|
|
public function handleShowRecordsList(array $callback, Player $player) {
|
|
$this->showRecordsList(array(), $player);
|
|
}
|
|
|
|
/**
|
|
* Exports the Records of a map to a CSV-file
|
|
*
|
|
* @api
|
|
* @param array $chat
|
|
* @param Player $player
|
|
*/
|
|
public function exportRecordsList(array $chat, Player $player) {
|
|
if (!$this->maniaControl->getAuthenticationManager()->checkPluginPermission($this, $player, self::PERMISSION_DELETE_ANY_RECORD)) {
|
|
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
|
|
return;
|
|
}
|
|
|
|
$commandParts = explode(' ', $chat[1][2]);
|
|
if (count($commandParts) < 2 || strlen($commandParts[1]) == 0) {
|
|
$this->maniaControl->getChat()->sendUsageInfo('Missing CSV-Filename ID! (Example: //exportrecs locals.csv)', $player);
|
|
return;
|
|
}
|
|
|
|
$filename = $commandParts[1];
|
|
if (!DataUtil::endsWith($filename, '.csv')) {
|
|
$filename .= '.csv';
|
|
}
|
|
|
|
$map = null;
|
|
if (count($commandParts) >= 3) {
|
|
$mapId = (int) $commandParts[2];
|
|
if ($mapId <= 0) {
|
|
$this->maniaControl->getChat()->sendUsageInfo('Map-Id below 1!', $player);
|
|
return;
|
|
}
|
|
|
|
$mapIndex = $mapId-1;
|
|
$map = $this->maniaControl->getMapManager()->getMapByIndex($mapIndex);
|
|
if (!$map) {
|
|
$this->maniaControl->getChat()->sendUsageInfo('Map-Id too high!', $player);
|
|
return;
|
|
}
|
|
} else {
|
|
$map = $this->maniaControl->getMapManager()->getCurrentMap();
|
|
}
|
|
|
|
$records = $this->getLocalRecords($map);
|
|
$lines = array();
|
|
$header = array('rank', 'login', 'time', 'checkpoints');
|
|
array_push($lines, implode(self::CSV_SPLITTER, $header));
|
|
|
|
foreach ($records as $record) {
|
|
$line = array(
|
|
$record->rank,
|
|
$record->login,
|
|
$record->time,
|
|
$record->checkpoints,
|
|
);
|
|
array_push($lines, implode(self::CSV_SPLITTER, $line));
|
|
}
|
|
|
|
try {
|
|
file_put_contents($filename, implode(PHP_EOL, $lines));
|
|
} catch (\Exception $e) {
|
|
$this->maniaControl->getChat()->sendException($e, $player);
|
|
}
|
|
|
|
$this->maniaControl->getChat()->sendSuccess('Successfully exported Local Records of map ' . $map->getEscapedName() . ' to $<$fff' . $filename . '$>!');
|
|
}
|
|
|
|
/**
|
|
* Shows a ManiaLink list with the local records.
|
|
*
|
|
* @api
|
|
* @param array $chat
|
|
* @param Player $player
|
|
*/
|
|
public function showRecordsList(array $chat, Player $player) {
|
|
$width = $this->maniaControl->getManialinkManager()->getStyleManager()->getListWidgetsWidth();
|
|
$height = $this->maniaControl->getManialinkManager()->getStyleManager()->getListWidgetsHeight();
|
|
|
|
// get PlayerList
|
|
$records = $this->getLocalRecords($this->maniaControl->getMapManager()->getCurrentMap(), 200);
|
|
|
|
// 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;
|
|
|
|
// Predefine Description Label
|
|
$descriptionLabel = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultDescriptionLabel();
|
|
$frame->addChild($descriptionLabel);
|
|
|
|
// 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);
|
|
$labelLine->addLabelEntryText('Login', $posX + 70);
|
|
$labelLine->addLabelEntryText('Time', $posX + 101);
|
|
$labelLine->render();
|
|
|
|
$index = 0;
|
|
$posY = $height / 2 - 10;
|
|
$pageFrame = null;
|
|
|
|
foreach ($records as $listRecord) {
|
|
if ($index % 15 === 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);
|
|
}
|
|
|
|
if ($listRecord->login === $player->login) {
|
|
$currentQuad = new Quad_Icons64x64_1();
|
|
$recordFrame->addChild($currentQuad);
|
|
$currentQuad->setX($posX + 3.5);
|
|
$currentQuad->setSize(4, 4);
|
|
$currentQuad->setSubStyle($currentQuad::SUBSTYLE_ArrowBlue);
|
|
}
|
|
|
|
if (strlen($listRecord->nickname) < 2) {
|
|
$listRecord->nickname = $listRecord->login;
|
|
}
|
|
|
|
$labelLine = new LabelLine($recordFrame);
|
|
$labelLine->addLabelEntryText($listRecord->rank, $posX + 5, 13);
|
|
$labelLine->addLabelEntryText('$fff' . $listRecord->nickname, $posX + 18, 52);
|
|
$labelLine->addLabelEntryText($listRecord->login, $posX + 70, 31);
|
|
$labelLine->addLabelEntryText(Formatter::formatTime($listRecord->time), $posX + 101, $width / 2 - ($posX + 110));
|
|
$labelLine->render();
|
|
|
|
$recordFrame->setY($posY);
|
|
|
|
$posY -= 4;
|
|
$index++;
|
|
}
|
|
|
|
// Render and display xml
|
|
$this->maniaControl->getManialinkManager()->displayWidget($maniaLink, $player, 'LocalRecords');
|
|
}
|
|
|
|
/**
|
|
* Delete any record
|
|
*
|
|
* @internal
|
|
* @param array $chat
|
|
* @param Player $player
|
|
*/
|
|
public function deleteAnyRecord(array $chat, Player $player) {
|
|
if (!$this->maniaControl->getAuthenticationManager()->checkPluginPermission($this, $player, self::PERMISSION_DELETE_ANY_RECORD)) {
|
|
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
|
|
return;
|
|
}
|
|
|
|
$commandParts = explode(' ', $chat[1][2]);
|
|
if (count($commandParts) < 2 || strlen($commandParts[1]) == 0) {
|
|
$this->maniaControl->getChat()->sendUsageInfo('Missing Record ID! (Example: //delrec 3)', $player);
|
|
return;
|
|
}
|
|
|
|
$recordRank = (int) $commandParts[1];
|
|
$currentMap = $this->maniaControl->getMapManager()->getCurrentMap();
|
|
$records = $this->getLocalRecords($currentMap);
|
|
if ($recordRank <= 0 || count($records) < $recordRank) {
|
|
$this->maniaControl->getChat()->sendError('Cannot remove record $<$fff' . $recordRank . '$>!', $player);
|
|
return;
|
|
}
|
|
|
|
assert($recordRank == $records[$recordRank-1]->rank);
|
|
$playerIndex = $records[$recordRank-1]->playerIndex;
|
|
$playerNick = $records[$recordRank-1]->nickname;
|
|
|
|
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
|
|
$query = "DELETE FROM `" . self::TABLE_RECORDS . "`
|
|
WHERE `mapIndex` = {$currentMap->index}
|
|
AND `playerIndex` = {$playerIndex};";
|
|
$mysqli->query($query);
|
|
if ($mysqli->error) {
|
|
trigger_error($mysqli->error);
|
|
return;
|
|
}
|
|
|
|
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_LOCALRECORDS_CHANGED, null);
|
|
$this->maniaControl->getChat()->sendSuccess('Record no. $<$fff' . $recordRank . '$> by $<$fff' . $playerNick . '$> has been removed!');
|
|
}
|
|
|
|
/**
|
|
* Delete the personal record
|
|
*
|
|
* @internal
|
|
* @param array $chat
|
|
* @param Player $player
|
|
*/
|
|
public function deletePersonalRecord(array $chat, Player $player) {
|
|
if (!$this->maniaControl->getAuthenticationManager()->checkPluginPermission($this, $player, self::PERMISSION_DELETE_PERSONAL_RECORD)) {
|
|
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
|
|
return;
|
|
}
|
|
|
|
$currentMap = $this->maniaControl->getMapManager()->getCurrentMap();
|
|
$records = $this->getLocalRecords($currentMap);
|
|
$hasRecord = true;
|
|
foreach ($records as $record) {
|
|
if ($record->login === $player->login) {
|
|
$hasRecord = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!$hasRecord) {
|
|
$this->maniaControl->getChat()->sendError('You have no personal record to remove!', $player);
|
|
return;
|
|
}
|
|
|
|
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
|
|
$query = "DELETE FROM `" . self::TABLE_RECORDS . "`
|
|
WHERE `mapIndex` = {$currentMap->index}
|
|
AND `playerIndex` = {$player->index};";
|
|
$mysqli->query($query);
|
|
if ($mysqli->error) {
|
|
trigger_error($mysqli->error);
|
|
return;
|
|
}
|
|
|
|
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_LOCALRECORDS_CHANGED, null);
|
|
$this->maniaControl->getChat()->sendSuccess('$<$fff'.$player->getEscapedNickname().'$> removed his personal record!');
|
|
}
|
|
|
|
/**
|
|
* Update the RecordWidget variables
|
|
*/
|
|
private function updateRecordWidget() {
|
|
$width = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_WIDGET_WIDTH);
|
|
$lineHeight = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_WIDGET_LINEHEIGHT);
|
|
|
|
$this->recordWidget->setWidth($width);
|
|
$this->recordWidget->setLineHeight($lineHeight);
|
|
$this->updateManialink = true;
|
|
}
|
|
} |