From fa7ea9f4f3f1840dbe93440f420e86ee273c4cc5 Mon Sep 17 00:00:00 2001 From: axelalex2 <39660135+axelalex2@users.noreply.github.com> Date: Mon, 24 Feb 2020 17:20:51 +0100 Subject: [PATCH] Multiple Features to improve ManiaControl usability (#234) * ServerUIPropertiesMenu for Configurator to edit builtin UIProperties of MP * fixed unregister-functions of CallbackManager * Reducing menuItemHeight in Configurator to avoid overlapping of the menu items * Fully rebuild the admins menu after a player rights changed * Added function to FileUtil to improve realpath, so symbolic links can be resolved * Fixed indentation * Update FileUtil.php Fixed error in case of an absolute path on Unix-like systems. * New CallQueue which runs once inbetween the MC-loops * Added important queued call to be executed earlier * ErrorMethod made optional, as in some cases, there might be nothing to fail * Clean up repository from unnecessary files * Added easy installation script for DB on Unix-like systems * Replaced deprecated is_real by is_float * Add milliseconds with dot instead of double colon * Resolved deprecated curly braces error * gitignore all hidden files (except git and gitignore) * Update MC-update-check-interval, so that a restart is not required * Update gitignore to not ignore changes in MCTeam-Plugins * Update gitignore again * And another try * fixed MasterAdmin //delrec, added personal /delrec-command with permission-setting * Increase version number of local records plugin * Add Permission to delete any record * Reworked notifications of locals, removed private only, added private at rank * Fix formatting * Allow AuthenticationManager to store Plugin Permissions * Method to check, if a named function call is already queued * Added command disable feature * Reset timer if DeltaTime updated, so it does not try to catch up missed timings * Added private notification setting * To reduce load of multiple records (especially in rounds), queue call chat notifications * Added internal function to plugin manager to return plugin menu * restore .idea codestyle files * Update MC-Version number to 0.250 --- .gitignore | 11 +- ManiaControl.sh | 0 core/Admin/AuthenticationManager.php | 106 +++++++++-- core/Callbacks/CallQueueListener.php | 13 ++ core/Callbacks/CallQueueListening.php | 69 +++++++ core/Callbacks/CallQueueManager.php | 117 ++++++++++++ core/Callbacks/TimerListening.php | 10 + core/Callbacks/TimerManager.php | 18 ++ core/Commands/CommandManager.php | 153 ++++++++++++++- core/ManiaControl.php | 20 +- core/Plugins/PluginManager.php | 8 + core/Settings/Setting.php | 2 +- core/Statistics/SimpleStatsList.php | 2 +- core/Update/UpdateManager.php | 12 ++ core/Utils/Formatter.php | 2 +- install_db.sh | 24 +++ plugins/MCTeam/Dedimania/DedimaniaPlugin.php | 39 ++-- plugins/MCTeam/LocalRecordsPlugin.php | 185 +++++++++++++------ 18 files changed, 700 insertions(+), 91 deletions(-) mode change 100644 => 100755 ManiaControl.sh create mode 100755 core/Callbacks/CallQueueListener.php create mode 100755 core/Callbacks/CallQueueListening.php create mode 100755 core/Callbacks/CallQueueManager.php create mode 100755 install_db.sh diff --git a/.gitignore b/.gitignore index 3d3c3abe..12e91612 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,11 @@ -.idea -*.iml +.* +!/.git +!/.gitignore +!/.idea /configs/server.xml /logs/ /ManiaControl.log /ManiaControl.pid -!/.idea/encodings.xml -!/.idea/codeStyleSettings.xml +!/plugins/ +/plugins/* +!/plugins/MCTeam/ diff --git a/ManiaControl.sh b/ManiaControl.sh old mode 100644 new mode 100755 diff --git a/core/Admin/AuthenticationManager.php b/core/Admin/AuthenticationManager.php index a69b35de..dd660b6c 100644 --- a/core/Admin/AuthenticationManager.php +++ b/core/Admin/AuthenticationManager.php @@ -14,6 +14,7 @@ use ManiaControl\Logger; use ManiaControl\ManiaControl; use ManiaControl\Players\Player; use ManiaControl\Players\PlayerManager; +use ManiaControl\Plugins\Plugin; use ManiaControl\Settings\Setting; /** @@ -133,8 +134,10 @@ class AuthenticationManager implements CallbackListener, EchoListener, Communica return self::AUTH_NAME_ADMIN; case self::AUTH_LEVEL_MODERATOR: return self::AUTH_NAME_MODERATOR; + case self::AUTH_LEVEL_PLAYER: + return self::AUTH_NAME_PLAYER; } - return self::AUTH_NAME_PLAYER; + return '-'; } /** @@ -172,8 +175,10 @@ class AuthenticationManager implements CallbackListener, EchoListener, Communica return self::AUTH_LEVEL_ADMIN; case self::AUTH_NAME_MODERATOR: return self::AUTH_LEVEL_MODERATOR; + case self::AUTH_NAME_PLAYER: + return self::AUTH_LEVEL_PLAYER; } - return self::AUTH_LEVEL_PLAYER; + return -1; } /** @@ -420,7 +425,21 @@ class AuthenticationManager implements CallbackListener, EchoListener, Communica */ public function checkPermission(Player $player, $rightName) { $right = $this->maniaControl->getSettingManager()->getSettingValue($this, $rightName); - return $this->checkRight($player, $this->getAuthLevel($right)); + return self::checkRight($player, self::getAuthLevel($right)); + } + + /** + * Checks the permission by a right name + * + * @api + * @param Plugin $plugin + * @param Player $player + * @param $rightName + * @return bool + */ + public function checkPluginPermission(Plugin $plugin, Player $player, $rightName) { + $right = $this->maniaControl->getSettingManager()->getSettingValue($plugin, $rightName); + return self::checkRight($player, self::getAuthLevel($right)); } /** @@ -429,9 +448,23 @@ class AuthenticationManager implements CallbackListener, EchoListener, Communica * @api * @param string $rightName * @param int $authLevelNeeded + * @param string $authLevelsAllowed */ - public function definePermissionLevel($rightName, $authLevelNeeded) { - $this->maniaControl->getSettingManager()->initSetting($this, $rightName, self::getPermissionLevelNameArray($authLevelNeeded)); + public function definePermissionLevel($rightName, $authLevelNeeded, $authLevelsAllowed = self::AUTH_LEVEL_MODERATOR) { + $this->maniaControl->getSettingManager()->initSetting($this, $rightName, self::getPermissionLevelNameArray($authLevelNeeded, $authLevelsAllowed)); + } + + /** + * Define a Minimum Right Level needed for an Action + * + * @api + * @param Plugin $plugin + * @param string $rightName + * @param int $authLevelNeeded + * @param string $authLevelsAllowed + */ + public function definePluginPermissionLevel(Plugin $plugin, $rightName, $authLevelNeeded, $authLevelsAllowed = self::AUTH_LEVEL_MODERATOR) { + $this->maniaControl->getSettingManager()->initSetting($plugin, $rightName, self::getPermissionLevelNameArray($authLevelNeeded, $authLevelsAllowed)); } /** @@ -439,19 +472,70 @@ class AuthenticationManager implements CallbackListener, EchoListener, Communica * * @api * @param $authLevelNeeded + * @param $authLevelsAllowed * @return array[] */ - public static function getPermissionLevelNameArray($authLevelNeeded) { - switch ($authLevelNeeded) { + public static function getPermissionLevelNameArray($authLevelNeeded, $authLevelsAllowed = self::AUTH_LEVEL_MODERATOR) { + assert($authLevelNeeded >= $authLevelsAllowed); + + switch ($authLevelsAllowed) { + case self::AUTH_LEVEL_PLAYER: + switch ($authLevelNeeded) { + case self::AUTH_LEVEL_PLAYER: + return array(self::AUTH_NAME_PLAYER, self::AUTH_NAME_MODERATOR, self::AUTH_NAME_ADMIN, self::AUTH_NAME_SUPERADMIN, self::AUTH_NAME_MASTERADMIN); + case self::AUTH_LEVEL_MODERATOR: + return array(self::AUTH_NAME_MODERATOR, self::AUTH_NAME_ADMIN, self::AUTH_NAME_SUPERADMIN, self::AUTH_NAME_MASTERADMIN, self::AUTH_NAME_PLAYER); + case self::AUTH_LEVEL_ADMIN: + return array(self::AUTH_NAME_ADMIN, self::AUTH_NAME_SUPERADMIN, self::AUTH_NAME_MASTERADMIN, self::AUTH_NAME_PLAYER, self::AUTH_NAME_MODERATOR); + case self::AUTH_LEVEL_SUPERADMIN: + return array(self::AUTH_NAME_SUPERADMIN, self::AUTH_NAME_MASTERADMIN, self::AUTH_NAME_PLAYER, self::AUTH_NAME_MODERATOR, self::AUTH_NAME_ADMIN); + case self::AUTH_LEVEL_MASTERADMIN: + return array(self::AUTH_NAME_MASTERADMIN, self::AUTH_NAME_PLAYER, self::AUTH_NAME_MODERATOR, self::AUTH_NAME_ADMIN, self::AUTH_NAME_SUPERADMIN); + } + break; + case self::AUTH_LEVEL_MODERATOR: - return array(self::AUTH_NAME_MODERATOR, self::AUTH_NAME_ADMIN, self::AUTH_NAME_SUPERADMIN, self::AUTH_NAME_MASTERADMIN); + switch ($authLevelNeeded) { + case self::AUTH_LEVEL_MODERATOR: + return array(self::AUTH_NAME_MODERATOR, self::AUTH_NAME_ADMIN, self::AUTH_NAME_SUPERADMIN, self::AUTH_NAME_MASTERADMIN); + case self::AUTH_LEVEL_ADMIN: + return array(self::AUTH_NAME_ADMIN, self::AUTH_NAME_SUPERADMIN, self::AUTH_NAME_MASTERADMIN, self::AUTH_NAME_MODERATOR); + case self::AUTH_LEVEL_SUPERADMIN: + return array(self::AUTH_NAME_SUPERADMIN, self::AUTH_NAME_MASTERADMIN, self::AUTH_NAME_MODERATOR, self::AUTH_NAME_ADMIN); + case self::AUTH_LEVEL_MASTERADMIN: + return array(self::AUTH_NAME_MASTERADMIN, self::AUTH_NAME_MODERATOR, self::AUTH_NAME_ADMIN, self::AUTH_NAME_SUPERADMIN); + } + break; + case self::AUTH_LEVEL_ADMIN: - return array(self::AUTH_NAME_ADMIN, self::AUTH_NAME_SUPERADMIN, self::AUTH_NAME_MASTERADMIN, self::AUTH_NAME_MODERATOR); + switch ($authLevelNeeded) { + case self::AUTH_LEVEL_ADMIN: + return array(self::AUTH_NAME_ADMIN, self::AUTH_NAME_SUPERADMIN, self::AUTH_NAME_MASTERADMIN); + case self::AUTH_LEVEL_SUPERADMIN: + return array(self::AUTH_NAME_SUPERADMIN, self::AUTH_NAME_MASTERADMIN, self::AUTH_NAME_ADMIN); + case self::AUTH_LEVEL_MASTERADMIN: + return array(self::AUTH_NAME_MASTERADMIN, self::AUTH_NAME_ADMIN, self::AUTH_NAME_SUPERADMIN); + } + break; + case self::AUTH_LEVEL_SUPERADMIN: - return array(self::AUTH_NAME_SUPERADMIN, self::AUTH_NAME_MASTERADMIN, self::AUTH_NAME_MODERATOR, self::AUTH_NAME_ADMIN); + switch ($authLevelNeeded) { + case self::AUTH_LEVEL_SUPERADMIN: + return array(self::AUTH_NAME_SUPERADMIN, self::AUTH_NAME_MASTERADMIN); + case self::AUTH_LEVEL_MASTERADMIN: + return array(self::AUTH_NAME_MASTERADMIN, self::AUTH_NAME_SUPERADMIN); + } + break; + + // just for completeness, should not be used this way case self::AUTH_LEVEL_MASTERADMIN: - return array(self::AUTH_NAME_MASTERADMIN, self::AUTH_NAME_MODERATOR, self::AUTH_NAME_ADMIN, self::AUTH_NAME_SUPERADMIN); + switch ($authLevelNeeded) { + case self::AUTH_LEVEL_MASTERADMIN: + return array(self::AUTH_NAME_MASTERADMIN); + } + break; } + return array("-"); } } diff --git a/core/Callbacks/CallQueueListener.php b/core/Callbacks/CallQueueListener.php new file mode 100755 index 00000000..05f15cc2 --- /dev/null +++ b/core/Callbacks/CallQueueListener.php @@ -0,0 +1,13 @@ + + * @copyright 2014-2019 ManiaControl Team + * @license http://www.gnu.org/licenses/ GNU General Public License, Version 3 + */ +interface CallQueueListener { +} diff --git a/core/Callbacks/CallQueueListening.php b/core/Callbacks/CallQueueListening.php new file mode 100755 index 00000000..41371a5d --- /dev/null +++ b/core/Callbacks/CallQueueListening.php @@ -0,0 +1,69 @@ + + * @copyright 2014-2019 ManiaControl Team + * @license http://www.gnu.org/licenses/ GNU General Public License, Version 3 + */ +class CallQueueListening extends Listening implements UsageInformationAble { + use UsageInformationTrait; + + private $errorMethod = null; + + /** + * Construct a new Call Queue Listening + * + * @param CallQueueListener $listener + * @param mixed $method + * @param mixed $errorMethod + */ + public function __construct(CallQueueListener $listener, $method, $errorMethod) { + parent::__construct($listener, $method); + if ($errorMethod != null) { + $this->errorMethod = array($listener, $errorMethod); + } + } + + /** + * Trigger the Listener's Method + * @return bool + */ + public function triggerCallback() { + $params = func_get_args(); + if ($this->triggerCallbackWithParams($params, false) === false) { + if ($this->$errorMethod != null) { + call_user_func($this->errorMethod, $this->method); + } + + return false; + } + + return true; + } + + /** + * Trigger the Listener's Method with the given Array of Params + * + * @param array $params + * @return mixed + */ + public function triggerCallbackWithParams(array $params, bool $callErrorMethod = true) { + $result = call_user_func_array($this->getUserFunction(), $params); + if ($callErrorMethod && $result === false) { + if ($this->errorMethod != null) { + call_user_func($this->errorMethod, $this->getUserFunction()); + } + + return false; + } + + return $result; + } +} diff --git a/core/Callbacks/CallQueueManager.php b/core/Callbacks/CallQueueManager.php new file mode 100755 index 00000000..14f13a7c --- /dev/null +++ b/core/Callbacks/CallQueueManager.php @@ -0,0 +1,117 @@ + + * @copyright 2014-2019 ManiaControl Team + * @license http://www.gnu.org/licenses/ GNU General Public License, Version 3 + */ +class CallQueueManager implements UsageInformationAble { + use UsageInformationTrait; + + /* + * Private properties + */ + /** @var ManiaControl $maniaControl */ + private $maniaControl = null; + /** @var CallQueueListening[] $queueListenings */ + private $queueListenings = array(); + + /** + * Construct a new Call Queue Manager + * + * @param ManiaControl $maniaControl + */ + public function __construct(ManiaControl $maniaControl) { + $this->maniaControl = $maniaControl; + } + + /** + * Register a Call Queue Listening + * + * @param CallQueueListener $listener + * @param mixed $methods + * @param mixed $errorMethod + * @return bool + */ + public function registerListening(CallQueueListener $listener, $methods, $errorMethod = null, bool $important = false) { + if ($errorMethod != null && !CallQueueListening::checkValidCallback($listener, $errorMethod)) { + trigger_error("Given Listener (" . get_class($listener) . ") can't handle Queue Call Callback (No Error Method '{$errorMethod}')!"); + return false; + } + + if (!is_array($methods)) { + $methods = array($methods); + } + + foreach ($methods as $method) { + if (!CallQueueListening::checkValidCallback($listener, $method)) { + trigger_error("Given Listener (" . get_class($listener) . ") can't handle Queue Call Callback (No Method '{$method}')!"); + return false; + } + } + + foreach ($methods as $method) { + // Build Call Queue Listening + $listening = new CallQueueListening($listener, $method, $errorMethod); + if ($important) { + $this->addImportantListening($listening); + } else { + $this->addListening($listening); + } + } + + return true; + } + + /** + * Adds an important Listening to the current list of managed queue calls at the front + * + * @param CallQueueListening $queueListening + */ + public function addImportantListening(CallQueueListening $queueListening) { + array_unshift($this->queueListenings, $queueListening); + } + + /** + * Adds a Listening to the current list of managed queue calls at the end + * + * @param CallQueueListening $queueListening + */ + public function addListening(CallQueueListening $queueListening) { + array_push($this->queueListenings, $queueListening); + } + + /** + * Checks, if one specific listening already has been queued for a call. + * Can only check for named functions. + * @param CallQueueListener $listener + * @param string $method + * @return bool + */ + public function hasListening(CallQueueListener $listener, string $method) { + foreach ($this->queueListenings as $listening) { + if ($listening->listener === $listener && $listening->method === $method) { + return true; + } + } + return false; + } + + /** + * Manage one of the queued calls + */ + public function manageCallQueue() { + if (!empty($this->queueListenings)) { + $listening = array_shift($this->queueListenings); + $listening->triggerCallback(); + } + } +} diff --git a/core/Callbacks/TimerListening.php b/core/Callbacks/TimerListening.php index a723e14c..4442b1da 100644 --- a/core/Callbacks/TimerListening.php +++ b/core/Callbacks/TimerListening.php @@ -57,6 +57,16 @@ class TimerListening extends Listening implements UsageInformationAble { } } + /** + * Set the deltaTime + * + * @param float $milliSeconds + */ + public function setDeltaTime($milliSeconds) { + $this->deltaTime = $milliSeconds / 1000.; + $this->lastTrigger = null; + } + /** * Check if the desired Time is reached * diff --git a/core/Callbacks/TimerManager.php b/core/Callbacks/TimerManager.php index 1eefdc9d..63fd9080 100644 --- a/core/Callbacks/TimerManager.php +++ b/core/Callbacks/TimerManager.php @@ -75,6 +75,24 @@ class TimerManager implements UsageInformationAble { array_push($this->timerListenings, $timerListening); } + /** + * Update the deltaTime of a Timer Listening + * + * @param TimerListener $listener + * @param string|callable $method + * @param float $milliSeconds + */ + public function updateTimerListening(TimerListener $listener, $method, $milliSeconds) { + $updated = false; + foreach ($this->timerListenings as $key => &$listening) { + if ($listening->listener === $listener && $listening->method === $method) { + $listening->setDeltaTime($milliSeconds); + $updated = true; + } + } + return $updated; + } + /** * Unregister a Timer Listening * diff --git a/core/Commands/CommandManager.php b/core/Commands/CommandManager.php index 93363ec9..72389ad2 100644 --- a/core/Commands/CommandManager.php +++ b/core/Commands/CommandManager.php @@ -28,8 +28,12 @@ class CommandManager implements CallbackListener, UsageInformationAble { private $helpManager = array(); /** @var Listening[][] $commandListenings */ private $commandListenings = array(); + /** @var CommandListener[][] $disabledCommands */ + private $disabledCommands = array(); /** @var Listening[][] $adminCommandListenings */ private $adminCommandListenings = array(); + /** @var CommandListener[][] $disabledAdminCommands */ + private $disabledAdminCommands = array(); /** * Construct a new Commands Manager @@ -116,6 +120,138 @@ class CommandManager implements CallbackListener, UsageInformationAble { array_push($listeningsArray[$command], $listening); } + /** + * Disable the command(s) by the given listener. + * The specific listener has to also manually reenable the commands, before the command can be used again. + * @param mixed $commandName + * @param bool $adminCommand + * @param CommandListener $listener + */ + public function disableCommand($commandName, bool $adminCommand, CommandListener $listener) { + if (is_array($commandName)) { + foreach ($commandName as $command) { + $this->disableCommand($command, $adminCommand, $listener); + } + return; + } + + $command = strtolower(trim($commandName)); + // first, check if the command actually exists + if (!array_key_exists($command, $this->commandListenings) && !array_key_exists($command, $this->adminCommandListenings)) { + return; + } + + $disabledCommands = null; + if ($adminCommand) { + $disabledCommands = &$this->disabledAdminCommands; + } else { + $disabledCommands = &$this->disabledCommands; + } + + if (!array_key_exists($command, $disabledCommands)) { + $disabledCommands[$command] = array(); + } + + if (!in_array($listener, $disabledCommands[$command])) { + array_push($disabledCommands[$command], $listener); + } + } + + /** + * Enable the command(s) by the given listener. + * @param mixed $commandName + * @param bool $adminCommand + * @param CommandListener $listener + */ + public function enableCommand($commandName, bool $adminCommand, CommandListener $listener) { + if (is_array($commandName)) { + foreach ($commandName as $command) { + $this->enableCommand($command, $adminCommand, $listener); + } + return; + } + + $command = strtolower(trim($commandName)); + + $disabledCommands = null; + if ($adminCommand) { + $disabledCommands = &$this->disabledAdminCommands; + } else { + $disabledCommands = &$this->disabledCommands; + } + + if (!array_key_exists($command, $disabledCommands)) { + return; + } + + if (($key = array_search($listener, $disabledCommands[$command])) !== false) { + unset($disabledCommands[$command][$key]); + if (empty($disabledCommands[$command])) { + unset($disabledCommands[$command]); + } + } + } + + /** + * Checks if a command is enabled. + * @param mixed $commandName + * @param bool $adminCommand + * @return bool|array + */ + public function isCommandEnabled($commandName, bool $adminCommand) { + if (is_array($commandName)) { + $results = array(); + foreach ($commandName as $command) { + array_push($results, $this->isCommandEnabled($command, $adminCommand)); + } + $resultsUnique = array_unique($results); + if (count($resultsUnique) === 1) { + return $resultsUnique[0]; + } + + return $results; + } + + $command = strtolower(trim($commandName)); + + $disabledCommands = null; + if ($adminCommand) { + $disabledCommands = &$this->disabledAdminCommands; + } else { + $disabledCommands = &$this->disabledCommands; + } + + if (!array_key_exists($command, $disabledCommands)) { + return true; + } + + // if the command is disabled, there should be at least one listener in the array + assert(!empty($disabledCommands[$command])); + return false; + } + + /** + * Removes the given CommandListener blocking commands. + * + * @param array &$disabledCommands + * @param CommandListener $listener + * @return bool + */ + private function removeDisabledCommandListener(array &$disabledCommands, CommandListener $listener) { + $removed = false; + foreach ($disabledCommands as $command => $disableListeners) { + if (($key = array_search($listener, $disableListeners)) !== false) { + unset($disabledCommands[$command][$key]); + $removed = true; + + if (empty($disabledCommands[$command])) { + unset($disabledCommands[$command]); + } + } + } + return $removed; + } + /** * Unregister a Command Listener * @@ -130,6 +266,12 @@ class CommandManager implements CallbackListener, UsageInformationAble { if ($this->removeCommandListener($this->adminCommandListenings, $listener)) { $removed = true; } + if ($this->removeDisabledCommandListener($this->disabledCommands, $listener)) { + $removed = true; + } + if ($this->removeDisabledCommandListener($this->disabledAdminCommands, $listener)) { + $removed = true; + } return $removed; } @@ -178,9 +320,11 @@ class CommandManager implements CallbackListener, UsageInformationAble { if (!$command) { return; } - + + $isAdminCommand = null; if (substr($message, 0, 2) === '//' || $command === 'admin') { // Admin command + $isAdminCommand = true; $commandListenings = $this->adminCommandListenings; if ($command === 'admin') { @@ -197,6 +341,7 @@ class CommandManager implements CallbackListener, UsageInformationAble { $callback[1][2] = $message; } else { // User command + $isAdminCommand = false; $commandListenings = $this->commandListenings; } @@ -205,6 +350,12 @@ class CommandManager implements CallbackListener, UsageInformationAble { return; } + if (!$this->isCommandEnabled($command, $isAdminCommand)) { + $prefix = $isAdminCommand ? '//' : '/'; + $this->maniaControl->getChat()->sendError('The command $<$fff'.$prefix.$command.'$> is currently disabled!', $player); + return; + } + // Inform command listeners foreach ($commandListenings[$command] as $listening) { /** @var Listening $listening */ diff --git a/core/ManiaControl.php b/core/ManiaControl.php index f31cce1c..12c84743 100644 --- a/core/ManiaControl.php +++ b/core/ManiaControl.php @@ -8,6 +8,7 @@ use ManiaControl\Bills\BillManager; use ManiaControl\Callbacks\CallbackListener; use ManiaControl\Callbacks\CallbackManager; use ManiaControl\Callbacks\Callbacks; +use ManiaControl\Callbacks\CallQueueManager; use ManiaControl\Callbacks\EchoManager; use ManiaControl\Callbacks\TimerListener; use ManiaControl\Callbacks\TimerManager; @@ -52,7 +53,7 @@ class ManiaControl implements CallbackListener, CommandListener, TimerListener, /* * Constants */ - const VERSION = '0.240'; + const VERSION = '0.250'; const API_VERSION = '2013-04-16'; const MIN_DEDIVERSION = '2017-05-03_21_00'; const SCRIPT_TIMEOUT = 40; @@ -84,6 +85,10 @@ class ManiaControl implements CallbackListener, CommandListener, TimerListener, * @see getCallbackManager() */ private $callbackManager = null; + /** @var CallQueueManager $callQueueManager + * @see getCallQueueManager() + */ + private $callQueueManager = null; /** @var Chat $chat * @see getChat() */ @@ -180,6 +185,7 @@ class ManiaControl implements CallbackListener, CommandListener, TimerListener, // Load ManiaControl Modules $this->callbackManager = new CallbackManager($this); + $this->callQueueManager = new CallQueueManager($this); $this->modeScriptEventManager = new ModeScriptEventManager($this); $this->echoManager = new EchoManager($this); $this->communicationManager = new CommunicationManager($this); @@ -298,6 +304,15 @@ class ManiaControl implements CallbackListener, CommandListener, TimerListener, return $this->callbackManager; } + /** + * Return the call queue manager + * + * @return CallQueueManager + */ + public function getCallQueueManager() { + return $this->callQueueManager; + } + /** * Return the echo manager * @@ -705,6 +720,9 @@ class ManiaControl implements CallbackListener, CommandListener, TimerListener, // Manage callbacks $this->getCallbackManager()->manageCallbacks(); + // Manage queued calls + $this->getCallQueueManager()->manageCallQueue(); + // Manage async file reader $this->getFileReader()->appendData(); diff --git a/core/Plugins/PluginManager.php b/core/Plugins/PluginManager.php index 10c93f49..695b9276 100644 --- a/core/Plugins/PluginManager.php +++ b/core/Plugins/PluginManager.php @@ -561,6 +561,14 @@ class PluginManager { $asyncHttpRequest->getData(); } + /** + * @internal + * @return \ManiaControl\Plugins\PluginMenu + */ + public function getPluginMenu() { + return $this->pluginMenu; + } + /** * @internal * @return \ManiaControl\Plugins\InstallMenu diff --git a/core/Settings/Setting.php b/core/Settings/Setting.php index 29627f5d..8e4edb89 100644 --- a/core/Settings/Setting.php +++ b/core/Settings/Setting.php @@ -111,7 +111,7 @@ class Setting implements UsageInformationAble { if (is_int($value)) { return self::TYPE_INT; } - if (is_real($value)) { + if (is_float($value)) { return self::TYPE_REAL; } if (is_bool($value)) { diff --git a/core/Statistics/SimpleStatsList.php b/core/Statistics/SimpleStatsList.php index 30166b30..dfa979d9 100644 --- a/core/Statistics/SimpleStatsList.php +++ b/core/Statistics/SimpleStatsList.php @@ -237,7 +237,7 @@ class SimpleStatsList implements ManialinkPageAnswerListener, CallbackListener, } //Slice Array to chunk length - $statRankings[$order] = array_slice($statRankings{$order}, $playerBeginIndex, self::MAX_PAGES_PER_CHUNK * self::MAX_PLAYERS_PER_PAGE, true); + $statRankings[$order] = array_slice($statRankings[$order], $playerBeginIndex, self::MAX_PAGES_PER_CHUNK * self::MAX_PLAYERS_PER_PAGE, true); $pageNumber = 1 + $chunkIndex * self::MAX_PAGES_PER_CHUNK; foreach ($statRankings[$order] as $playerId => $value) { if ($index % self::MAX_PLAYERS_PER_PAGE === 1) { diff --git a/core/Update/UpdateManager.php b/core/Update/UpdateManager.php index 79e5ed6e..ca37d6ce 100644 --- a/core/Update/UpdateManager.php +++ b/core/Update/UpdateManager.php @@ -16,6 +16,8 @@ use ManiaControl\Logger; use ManiaControl\ManiaControl; use ManiaControl\Players\Player; use ManiaControl\Players\PlayerManager; +use ManiaControl\Settings\Setting; +use ManiaControl\Settings\SettingManager; /** * Manager checking for ManiaControl Core Updates @@ -72,6 +74,7 @@ class UpdateManager implements CallbackListener, CommandListener, TimerListener, $this->maniaControl->getTimerManager()->registerTimerListening($this, 'hourlyUpdateCheck', 1000 * 60 * 60 * $updateInterval); $this->maniaControl->getCallbackManager()->registerCallbackListener(PlayerManager::CB_PLAYERCONNECT, $this, 'handlePlayerJoined'); $this->maniaControl->getCallbackManager()->registerCallbackListener(PlayerManager::CB_PLAYERDISCONNECT, $this, 'handlePlayerDisconnect'); + $this->maniaControl->getCallbackManager()->registerCallbackListener(SettingManager::CB_SETTING_CHANGED, $this, 'handleSettingChanged'); // Permissions $this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_UPDATE, AuthenticationManager::AUTH_LEVEL_ADMIN); @@ -455,6 +458,15 @@ class UpdateManager implements CallbackListener, CommandListener, TimerListener, $this->checkAutoUpdate(); } + public function handleSettingChanged(Setting $setting) { + if (!$setting->setting != self::SETTING_UPDATECHECK_INTERVAL) { + return; + } + + $updateInterval = $setting->value; + $this->maniaControl->getTimerManager()->updateTimerListening($this, 'hourlyUpdateCheck', 1000 * 60 * 60, $updateInterval); + } + /** * Handle //checkupdate command * diff --git a/core/Utils/Formatter.php b/core/Utils/Formatter.php index 7df8937c..39900534 100644 --- a/core/Utils/Formatter.php +++ b/core/Utils/Formatter.php @@ -43,7 +43,7 @@ abstract class Formatter implements UsageInformationAble { $seconds -= ($hours * 60 + $minutes) * 60; $format = ($hours > 0 ? $hours . ':' : ''); $format .= ($hours > 0 && $minutes < 10 ? '0' : '') . $minutes . ':'; - $format .= ($seconds < 10 ? '0' : '') . $seconds . ':'; + $format .= ($seconds < 10 ? '0' : '') . $seconds . '.'; $format .= ($milliseconds < 100 ? '0' : '') . ($milliseconds < 10 ? '0' : '') . $milliseconds; return $format; } diff --git a/install_db.sh b/install_db.sh new file mode 100755 index 00000000..75c296bf --- /dev/null +++ b/install_db.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +read -e -p "Enter database name: " -i "maniacontrol_db" DATABASE_NAME +read -e -p "Enter user name: " -i "maniacontrol" USER_NAME +read -s -p "Enter user password: " USER_PASSWORD +echo +echo + +# Preparation +SQL_FILE_NAME="install_db.sql" +rm -f ${SQL_FILE_NAME} + +echo "Creating SQL script ..." +echo "CREATE DATABASE ${DATABASE_NAME} DEFAULT CHARACTER SET 'utf8mb4' DEFAULT COLLATE 'utf8mb4_unicode_ci';" >> ${SQL_FILE_NAME} +echo "CREATE USER '${USER_NAME}'@'localhost' IDENTIFIED BY '${USER_PASSWORD}';" >> ${SQL_FILE_NAME} +echo "GRANT ALL PRIVILEGES ON ${DATABASE_NAME}.* TO '${USER_NAME}'@'localhost';" >> ${SQL_FILE_NAME} + +echo "Executing SQL script as root ..." +sudo mysql -u root -p < ${SQL_FILE_NAME} + +echo "Cleaning up ..." +rm ${SQL_FILE_NAME} + +echo "Done." diff --git a/plugins/MCTeam/Dedimania/DedimaniaPlugin.php b/plugins/MCTeam/Dedimania/DedimaniaPlugin.php index 94f84aa7..48f81c31 100644 --- a/plugins/MCTeam/Dedimania/DedimaniaPlugin.php +++ b/plugins/MCTeam/Dedimania/DedimaniaPlugin.php @@ -36,21 +36,22 @@ class DedimaniaPlugin implements CallbackListener, CommandListener, TimerListene * Constants */ const ID = 8; - const VERSION = 0.5; + const VERSION = 0.6; const AUTHOR = 'MCTeam'; const NAME = 'Dedimania Plugin'; const MLID_DEDIMANIA = 'Dedimania.ManialinkId'; - const SETTING_WIDGET_ENABLE = 'Enable Dedimania Widget'; - 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_LINE_COUNT = 'Widget Displayed Lines Count'; - const SETTING_WIDGET_LINE_HEIGHT = 'Widget Line Height'; - const SETTING_DEDIMANIA_CODE = '$l[http://dedimania.net/tm2stats/?do=register]Dedimania Code for '; - const SETTING_NOTIFY_BEST_RECORDS = 'Notify Publicly only for the X Best Records'; - const SETTING_MAX_RECORDS = 'Max Records, only increase if you bought a rank update from Dedimania!'; + const SETTING_WIDGET_ENABLE = 'Enable Dedimania Widget'; + 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_LINE_COUNT = 'Widget Displayed Lines Count'; + const SETTING_WIDGET_LINE_HEIGHT = 'Widget Line Height'; + const SETTING_DEDIMANIA_CODE = '$l[http://dedimania.net/tm2stats/?do=register]Dedimania Code for '; + const SETTING_NOTIFY_BEST_RECORDS_PUBLIC = 'Notify Publicly only for the X Best Records'; + const SETTING_NOTIFY_BEST_RECORDS_PRIVATE = 'Notify Privately only for the X Best Records'; + const SETTING_MAX_RECORDS = 'Max Records, only increase if you bought a rank update from Dedimania!'; const CB_DEDIMANIA_CHANGED = 'Dedimania.Changed'; const CB_DEDIMANIA_UPDATED = 'Dedimania.Updated'; @@ -103,7 +104,8 @@ class DedimaniaPlugin implements CallbackListener, CommandListener, TimerListene $this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_WIDGET_WIDTH, 40); $this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_WIDGET_LINE_HEIGHT, 4); $this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_WIDGET_LINE_COUNT, 12); - $this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_NOTIFY_BEST_RECORDS, 30); + $this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_NOTIFY_BEST_RECORDS_PRIVATE, 30); + $this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_NOTIFY_BEST_RECORDS_PUBLIC, 15); $this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_MAX_RECORDS, 30); // Callbacks @@ -361,8 +363,6 @@ class DedimaniaPlugin implements CallbackListener, CommandListener, TimerListene $this->maniaControl->getCallbackManager()->triggerCallback(self::CB_DEDIMANIA_CHANGED, $newRecord); - $notifyOnlyBestRecords = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_NOTIFY_BEST_RECORDS); - // Announce record if ($oldRecord->nullRecord || $newRecord->rank < $oldRecord->rank) { // Gained rank @@ -372,21 +372,22 @@ class DedimaniaPlugin implements CallbackListener, CommandListener, TimerListene $improvement = 'improved the'; } - if ($newRecord->rank <= $notifyOnlyBestRecords) { + $notifyOnlyBestRecordsPrivate = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_NOTIFY_BEST_RECORDS_PRIVATE); + $notifyOnlyBestRecordsPublic = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_NOTIFY_BEST_RECORDS_PUBLIC); + if ($newRecord->rank <= $notifyOnlyBestRecordsPublic) { $notifyName = $player->nickname; - } else { + } elseif ($newRecord->rank <= $notifyOnlyBestRecordsPrivate) { $notifyName = 'You'; } - $message = '$390$<$fff' . $notifyName . '$> ' . $improvement . ' $<$ff0' . $newRecord->rank . '.$> Dedimania Record: $<$fff' . Formatter::formatTime($newRecord->best) . '$>'; if (!$oldRecord->nullRecord) { $message .= ' ($<$ff0' . $oldRecord->rank . '.$> $<$fff-' . Formatter::formatTime(($oldRecord->best - $recTime)) . '$>)'; } - if ($newRecord->rank <= $notifyOnlyBestRecords) { + if ($newRecord->rank <= $notifyOnlyBestRecordsPublic) { $this->maniaControl->getChat()->sendInformation($message . '!'); - } else { + } elseif ($newRecord->rank <= $notifyOnlyBestRecordsPrivate) { $this->maniaControl->getChat()->sendInformation($message . '!', $player); } diff --git a/plugins/MCTeam/LocalRecordsPlugin.php b/plugins/MCTeam/LocalRecordsPlugin.php index f5d21367..ce26e97e 100644 --- a/plugins/MCTeam/LocalRecordsPlugin.php +++ b/plugins/MCTeam/LocalRecordsPlugin.php @@ -12,6 +12,7 @@ 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; @@ -36,30 +37,33 @@ use MCTeam\Common\RecordWidget; * @copyright 2014-2019 ManiaControl Team * @license http://www.gnu.org/licenses/ GNU General Public License, Version 3 */ -class LocalRecordsPlugin implements ManialinkPageAnswerListener, CallbackListener, CommandListener, TimerListener, Plugin { +class LocalRecordsPlugin implements CallbackListener, CallQueueListener, CommandListener, ManialinkPageAnswerListener, TimerListener, Plugin { /* * Constants */ - const ID = 7; - const VERSION = 0.5; - const NAME = 'Local Records Plugin'; - const AUTHOR = 'MCTeam'; - const MLID_RECORDS = 'ml_local_records'; - const TABLE_RECORDS = 'mc_localrecords'; - 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_NOTIFY_ONLY_DRIVER = 'Notify only the Driver on New Records'; - const SETTING_NOTIFY_BEST_RECORDS = 'Notify Publicly only 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 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'; /* @@ -126,6 +130,19 @@ class LocalRecordsPlugin implements ManialinkPageAnswerListener, CallbackListene $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); @@ -133,8 +150,9 @@ class LocalRecordsPlugin implements ManialinkPageAnswerListener, CallbackListene $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_NOTIFY_ONLY_DRIVER, false); - $this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_NOTIFY_BEST_RECORDS, 10); + $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); @@ -154,7 +172,8 @@ class LocalRecordsPlugin implements ManialinkPageAnswerListener, CallbackListene $this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::TM_ONLAPFINISH, $this, 'handleFinishLapCallback'); $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, 'deleteRecord', true, 'Removes a record from the database.'); + $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'); @@ -493,8 +512,9 @@ class LocalRecordsPlugin implements ManialinkPageAnswerListener, CallbackListene $checkpointsString = $this->getCheckpoints($player->login); $this->checkpoints[$player->login] = array(); - $notifyOnlyDriver = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_NOTIFY_ONLY_DRIVER); - $notifyOnlyBestRecords = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_NOTIFY_BEST_RECORDS); + $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); @@ -502,23 +522,30 @@ class LocalRecordsPlugin implements ManialinkPageAnswerListener, CallbackListene if ($oldRecord->time < $time) { // Not improved return; - } - if ($oldRecord->time == $time) { + } else if ($oldRecord->time == $time) { // Same time - $message = '$3c0'; - if ($notifyOnlyDriver) { + 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 ($notifyOnlyDriver) { - $this->maniaControl->getChat()->sendInformation($message, $player); - } else if (!$notifyOnlyBestRecords || $oldRecord->rank <= $notifyOnlyBestRecords) { - $this->maniaControl->getChat()->sendInformation($message); + 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; } @@ -548,16 +575,14 @@ class LocalRecordsPlugin implements ManialinkPageAnswerListener, CallbackListene // Announce record $newRecord = $this->getLocalRecord($map, $player); - $improvedRank = (!$oldRecord || $newRecord->rank < $oldRecord->rank); + $improvedRank = ($oldRecord && $newRecord->rank >= $oldRecord->rank); - - $message = '$3c0'; - if ($notifyOnlyDriver) { + if ($notifyPubliclyAt < $newRecord->rank && $newRecord->rank <= $notifyPrivatelyAt) { $message .= 'You'; } else { $message .= '$<$fff' . $player->nickname . '$>'; } - $message .= ' ' . ($improvedRank ? 'gained' : 'improved') . ' the'; + $message .= ' ' . ($improvedRank ? 'improved' : 'gained') . ' the'; $message .= ' $<$ff0' . $newRecord->rank . '.$> Local Record:'; $message .= ' $<$fff' . Formatter::formatTime($newRecord->time) . '$>!'; if ($oldRecord) { @@ -569,10 +594,20 @@ class LocalRecordsPlugin implements ManialinkPageAnswerListener, CallbackListene $message .= '$<$fff-' . Formatter::formatTime($timeDiff) . '$>)'; } - if ($notifyOnlyDriver) { - $this->maniaControl->getChat()->sendInformation($message, $player); - } else if (!$notifyOnlyBestRecords || $newRecord->rank <= $notifyOnlyBestRecords) { - $this->maniaControl->getChat()->sendInformation($message); + 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); @@ -753,29 +788,75 @@ class LocalRecordsPlugin implements ManialinkPageAnswerListener, CallbackListene } /** - * Delete a Player's record + * Delete any record * * @internal * @param array $chat * @param Player $player */ - public function deleteRecord(array $chat, Player $player) { - if (!$this->maniaControl->getAuthenticationManager()->checkRight($player, AuthenticationManager::AUTH_LEVEL_MASTERADMIN)) { + 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) { + if (count($commandParts) < 2 || strlen($commandParts[1]) == 0) { $this->maniaControl->getChat()->sendUsageInfo('Missing Record ID! (Example: //delrec 3)', $player); return; } - $recordId = (int) $commandParts[1]; + $recordRank = (int) $commandParts[1]; $currentMap = $this->maniaControl->getMapManager()->getCurrentMap(); $records = $this->getLocalRecords($currentMap); - if (count($records) < $recordId) { - $this->maniaControl->getChat()->sendError('Cannot remove record $<$fff' . $recordId . '$>!', $player); + 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; } @@ -790,7 +871,7 @@ class LocalRecordsPlugin implements ManialinkPageAnswerListener, CallbackListene } $this->maniaControl->getCallbackManager()->triggerCallback(self::CB_LOCALRECORDS_CHANGED, null); - $this->maniaControl->getChat()->sendInformation('Record no. $<$fff' . $recordId . '$> has been removed!'); + $this->maniaControl->getChat()->sendSuccess('$<$fff'.$player->getEscapedNickname().'$> removed his personal record!'); } /**