diff --git a/plugins/MCTeam/GameModePresetsPlugin.php b/plugins/MCTeam/GameModePresetsPlugin.php new file mode 100644 index 00000000..bc900f0d --- /dev/null +++ b/plugins/MCTeam/GameModePresetsPlugin.php @@ -0,0 +1,380 @@ + + * @copyright 2014-2020 ManiaControl Team + * @license http://www.gnu.org/licenses/ GNU General Public License, Version 3 + */ +class GameModePresetsPlugin implements Plugin, CommandListener, TimerListener { + /* + * Constants + */ + const PLUGIN_ID = 9; + const PLUGIN_VERSION = 0.1; + const PLUGIN_NAME = 'GameMode Presets Plugin'; + const PLUGIN_AUTHOR = 'MCTeam'; + + const PRESET_SETTING_MODE_NUMBER = 'Mode Number'; + const PRESET_SETTING_SCRIPT_NAME = 'Script Name'; + + const SETTING_MAP_ACTION_ON_LOADMODE = 'Map Action on //loadmode'; + const SETTING_MAP_ACTION_DELAY_ON_LOADMODE = 'Map Action Delay on //loadmode (in ms)'; + const SETTING_PERMISSION_LOAD_GAMEMODE_PRESET = 'Permission load GameMode Preset'; + const SETTING_PERMISSION_SAVE_GAMEMODE_PRESET = 'Permission save GameMode Preset'; + + const MAP_ACTION_ON_LOADMODE_NONE = 'None'; + const MAP_ACTION_ON_LOADMODE_RESTART = 'Restart Map'; + const MAP_ACTION_ON_LOADMODE_SKIP = 'Skip Map'; + + const TABLE_GAMEMODEPRESETS = 'mc_gamemodepresets'; + + /* + * Private properties + */ + /** @var ManiaControl $maniaControl * */ + private $maniaControl = null; + + + /** + * @see \ManiaControl\Plugins\Plugin::prepare() + */ + public static function prepare(ManiaControl $maniaControl) { + } + + /** + * @see \ManiaControl\Plugins\Plugin::load() + */ + public function load(ManiaControl $maniaControl) { + $this->maniaControl = $maniaControl; + + // Authentication Permission Level + $this->maniaControl->getAuthenticationManager()->definePluginPermissionLevel( + $this, + self::SETTING_PERMISSION_LOAD_GAMEMODE_PRESET, + AuthenticationManager::AUTH_LEVEL_ADMIN, + AuthenticationManager::AUTH_LEVEL_MODERATOR + ); + $this->maniaControl->getAuthenticationManager()->definePluginPermissionLevel( + $this, + self::SETTING_PERMISSION_SAVE_GAMEMODE_PRESET, + AuthenticationManager::AUTH_LEVEL_SUPERADMIN, + AuthenticationManager::AUTH_LEVEL_MODERATOR + ); + + // Settings + $this->maniaControl->getSettingManager()->initSetting( + $this, + self::SETTING_MAP_ACTION_ON_LOADMODE, + array( + self::MAP_ACTION_ON_LOADMODE_NONE, + self::MAP_ACTION_ON_LOADMODE_RESTART, + self::MAP_ACTION_ON_LOADMODE_SKIP, + ) + ); + $this->maniaControl->getSettingManager()->initSetting( + $this, + self::SETTING_MAP_ACTION_DELAY_ON_LOADMODE, + 1000 + ); + + // Commands + $this->maniaControl->getCommandManager()->registerCommandListener(array('loadmode', 'modeload'), $this, 'commandLoadMode', true, 'Loads the mode settings from the given preset name.'); + $this->maniaControl->getCommandManager()->registerCommandListener(array('savemode', 'modesave'), $this, 'commandSaveMode', true, 'Saves the mode settings under the given preset name.'); + + $this->initTables(); + } + + /** + * Initialize needed database tables + */ + private function initTables() { + $mysqli = $this->maniaControl->getDatabase()->getMysqli(); + $query = "CREATE TABLE IF NOT EXISTS `" . self::TABLE_GAMEMODEPRESETS . "` ( + `index` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(20) NOT NULL, + `settings` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + PRIMARY KEY (`index`), + UNIQUE KEY (`name`) + ) 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); + } + } + + /** + * Fetch preset from database + * @param string $name + * @return array|null + */ + private function fetchPreset($name) { + $mysqli = $this->maniaControl->getDatabase()->getMysqli(); + $query = "SELECT `settings` + FROM `" . self::TABLE_GAMEMODEPRESETS . "` + WHERE `name` LIKE ?;"; + + $statement = $mysqli->prepare($query); + if ($mysqli->error || !$statement) { + trigger_error($mysqli->error, E_USER_ERROR); + return null; + } + + $statement->bind_param('s', $name); + $statement->execute(); + if ($statement->error) { + trigger_error($statement->error); + $statement->close(); + return null; + } + + $settings = null; + $statement->store_result(); + $statement->bind_result($settings); + $statement->fetch(); + $statement->free_result(); + + return json_decode($settings, true); + } + + /** + * Fetch preset from database + * @param string $name + * @return array|null + */ + private function storePreset($name) { + $modeNumber = $this->maniaControl->getClient()->getGameMode(); + $scriptName = $this->maniaControl->getClient()->getScriptName()['CurrentValue']; + $settings = null; + if ($modeNumber === 0) { + $settings = $this->maniaControl->getClient()->getModeScriptSettings(); + } else { + $settings = $this->maniaControl->getClient()->execute('GetCurrentGameInfo'); + } + + $settings[self::PRESET_SETTING_MODE_NUMBER] = $modeNumber; + $settings[self::PRESET_SETTING_SCRIPT_NAME] = $scriptName; + $settings = json_encode($settings); + + $mysqli = $this->maniaControl->getDatabase()->getMysqli(); + $query = "INSERT INTO `" . self::TABLE_GAMEMODEPRESETS . "` + (`name`, `settings`) VALUES (?, ?) + ON DUPLICATE KEY UPDATE `settings` = VALUES(`settings`);"; + + $statement = $mysqli->prepare($query); + if ($mysqli->error || !$statement) { + trigger_error($mysqli->error, E_USER_ERROR); + return false; + } + + $statement->bind_param('ss', $name, $settings); + $statement->execute(); + if ($statement->error) { + trigger_error($statement->error); + return false; + } + + $statement->close(); + return true; + } + + /** + * Load Script + * @param string $scriptName + */ + private function loadScript($scriptName) { + static $scriptsDir = null; + if ($scriptsDir === null) + { + $scriptsDataDir = FileUtil::shortenPath($this->maniaControl->getServer()->getDirectory()->getScriptsFolder()); + if ($this->maniaControl->getServer()->checkAccess($scriptsDataDir)) + { + $gameShort = $this->maniaControl->getMapManager()->getCurrentMap()->getGame(); + $game = ''; + switch ($gameShort) + { + case 'qm': $game = 'QuestMania'; break; + case 'sm': $game = 'ShootMania'; break; + case 'tm': $game = 'TrackMania'; break; + } + + if ($game != '') + { + $scriptsDir = $scriptsDataDir.DIRECTORY_SEPARATOR.'Modes'.DIRECTORY_SEPARATOR.$game.DIRECTORY_SEPARATOR; + if (!$this->maniaControl->getServer()->checkAccess($scriptsDir)) + $scriptsDir = null; + } + } + + if ($scriptsDir === null) + throw new \Exception('Scripts directory not found, unable to load different scripts!'); + } + + $scriptPath = $scriptsDir.$scriptName; + if (!file_exists($scriptPath)) + throw new \Exception('Script not found ('.$scriptPath.').'); + + $scriptText = file_get_contents($scriptPath); + + $this->maniaControl->getClient()->setModeScriptText($scriptText); + $this->maniaControl->getClient()->setScriptName($scriptName); + } + + /** + * Handle //loadmode command + * + * @param array $chatCallback + * @param \ManiaControl\Players\Player $player + */ + public function commandLoadMode(array $chatCallback, Player $player) { + if (!$this->maniaControl->getAuthenticationManager()->checkPluginPermission($this, $player, self::SETTING_PERMISSION_LOAD_GAMEMODE_PRESET)) { + $this->maniaControl->getAuthenticationManager()->sendNotAllowed($player); + return; + } + + // Check for delayed shutdown + $params = explode(' ', $chatCallback[1][2]); + if (count($params) < 2) { + $this->maniaControl->getChat()->sendError('You must provide a gamemode preset name to load settings from!', $player); + return; + } elseif (count($params) > 2) { + $this->maniaControl->getChat()->sendError('You can only provide one gamemode preset name to load settings from!', $player); + return; + } + + $presetName = strtolower($params[1]); + $presetSettings = $this->fetchPreset($presetName); + if (!$presetSettings) { + $this->maniaControl->getChat()->sendError('The gamemode preset $<$fff' . $presetName . '$> does not exist!', $player); + return; + } + + $modeNumber = $presetSettings[self::PRESET_SETTING_MODE_NUMBER]; + $scriptName = $presetSettings[self::PRESET_SETTING_SCRIPT_NAME]; + unset($presetSettings[self::PRESET_SETTING_MODE_NUMBER]); + unset($presetSettings[self::PRESET_SETTING_SCRIPT_NAME]); + + // this is a hack, because this setting always throws errors otherwise + $presetSettings['S_MatchmakingRematchRatio'] = floatval($presetSettings['S_MatchmakingRematchRatio']); + + try { + $this->maniaControl->getClient()->setGameMode($modeNumber); + if ($modeNumber === 0) { + $this->loadScript($scriptName); + $this->maniaControl->getClient()->setModeScriptSettings($presetSettings); + } else { + $this->maniaControl->getClient()->setScriptName($scriptName); + $this->maniaControl->getClient()->execute('SetGameInfos', $presetSettings); + } + } catch (\Exception $e) { + $this->maniaControl->getChat()->sendException($e, $player); + $this->maniaControl->getChat()->sendError('Unable to load gamemode preset $<$fff' . $presetName . '$>!', $player); + return; + } + + $this->maniaControl->getChat()->sendSuccess($player->getEscapedNickname() . ' loaded gamemode preset $<$fff' . $presetName . '$>!'); + $this->maniaControl->getTimerManager()->registerOneTimeListening( + $this, + function () { + $mapActionOnLoadmode = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_MAP_ACTION_ON_LOADMODE); + switch ($mapActionOnLoadmode) { + case self::MAP_ACTION_ON_LOADMODE_NONE: + break; + case self::MAP_ACTION_ON_LOADMODE_RESTART: + $this->maniaControl->getMapManager()->getMapActions()->restartMap(); + break; + case self::MAP_ACTION_ON_LOADMODE_SKIP: + $this->maniaControl->getMapManager()->getMapActions()->skipMap(); + break; + } + }, + $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_MAP_ACTION_DELAY_ON_LOADMODE) + ); + } + + /** + * Handle //savemode command + * + * @param array $chatCallback + * @param \ManiaControl\Players\Player $player + */ + public function commandSaveMode(array $chatCallback, Player $player) { + if (!$this->maniaControl->getAuthenticationManager()->checkPluginPermission($this, $player, self::SETTING_PERMISSION_LOAD_GAMEMODE_PRESET)) { + $this->maniaControl->getAuthenticationManager()->sendNotAllowed($player); + return; + } + + // Check for delayed shutdown + $params = explode(' ', $chatCallback[1][2]); + if (count($params) < 2) { + $this->maniaControl->getChat()->sendError('You must provide a gamemode preset name to save settings into!', $player); + return; + } elseif (count($params) > 2) { + $this->maniaControl->getChat()->sendError('You can only provide one gamemode preset name to save settings into!', $player); + return; + } + + $presetName = strtolower($params[1]); + try { + $this->storePreset($presetName); + } catch (\Exception $e) { + $this->maniaControl->getChat()->sendException($e, $player); + $this->maniaControl->getChat()->sendError('Unable to save gamemode preset $<$fff' . $presetName . '$>!', $player); + return; + } + + $this->maniaControl->getChat()->sendSuccess($player->getEscapedNickname() . ' saved gamemode settings in preset $<$fff"' . $presetName . '"$>!'); + } + + /** + * @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() { + return "Plugin offers presets functionalites for GameModes"; + } + + /** + * @see \ManiaControl\Plugins\Plugin::unload() + */ + public function unload() { + } +}