diff --git a/MatchManagerSuite/MatchManagerTMWTDuoIntegration.php b/MatchManagerSuite/MatchManagerTMWTDuoIntegration.php new file mode 100644 index 0000000..e6c1d3c --- /dev/null +++ b/MatchManagerSuite/MatchManagerTMWTDuoIntegration.php @@ -0,0 +1,466 @@ +maniaControl->getChat()->sendErrorToAdmins('MatchManager Core is required to use one of MatchManager plugin. Install it and restart Maniacontrol'); + Logger::logError('MatchManager Core is required to use one of MatchManager plugin. Install it and restart Maniacontrol'); + return false; +} +use MatchManagerSuite\MatchManagerCore; + + +/** + * MatchManager TMWT Duo Integration + * + * @author Beu (based on MatchManagerWidget by jonthekiller) + * @license http://www.gnu.org/licenses/ GNU General Public License, Version 3 + */ +class MatchManagerTMWTDuoIntegration implements CallbackListener, ManialinkPageAnswerListener, CommandListener, TimerListener, Plugin { + /* + * Constants + */ + const PLUGIN_ID = 211; + const PLUGIN_VERSION = 1.0; + const PLUGIN_NAME = 'MatchManager TMWT Duo Integration'; + const PLUGIN_AUTHOR = 'Beu'; + + // Other MatchManager plugin + const MATCHMANAGERCORE_PLUGIN = 'MatchManagerSuite\MatchManagerCore'; + const MATCHMANAGERADMINUI_PLUGIN = 'MatchManagerSuite\MatchManagerAdminUI'; + + // Actions + const ML_ACTION_OPENSETTINGS = 'MatchManagerSuite\MatchManagerTMWTDuoIntegration.OpenSettings'; + + const XMLRPC_CALLBACK_PICKANDBANCOMPLETE = 'PickBan.Complete'; + const XMLRPC_CALLBACK_STARTMAP = 'Maniaplanet.StartMap_Start'; + + const XMLRPC_METHOD_STARTPICKANDBAN = 'PickBan.Start'; + const XMLRPC_METHOD_ADDPLAYER = 'Club.Match.AddPlayer'; + const XMLRPC_METHOD_REMOVEPLAYER = 'Club.Match.RemovePlayer'; + const XMLRPC_METHOD_MATCHSTARTED = 'Club.Match.Start'; + const XMLRPC_METHOD_MATCHCOMPLETED = 'Club.Match.Completed'; + + const SETTING_TEAM1 = 'Team 1 Id'; + const SETTING_TEAM2 = 'Team 2 Id'; + const SETTING_PICKANDBAN_ENABLE = 'Enable Pick & Ban'; + const SETTING_PICKANDBAN_STEPCONFIG = 'Pick & Ban: Step config'; + const SETTING_PICKANDBAN_STEPDURATION = 'Pick & Ban: Step duration'; + const SETTING_PICKANDBAN_RESULTDURATION = 'Pick & Ban: Result duration'; + + const STATE_NOTHING = 0; + const STATE_PRESETTING = 1; + const STATE_PREMATCH = 2; + const STATE_MATCH = 3; + + /* + * Private properties + */ + /** @var ManiaControl $maniaControl */ + private $maniaControl = null; + private $MatchManagerCore = null; + private $state = 0; + + /** + * @param \ManiaControl\ManiaControl $maniaControl + * @see \ManiaControl\Plugins\Plugin::prepare() + */ + public static function prepare(ManiaControl $maniaControl) { + } + + /** + * @see \ManiaControl\Plugins\Plugin::getId() + */ + public static function getId() { + return self::PLUGIN_ID; + } + + /** + * @see \ManiaControl\Plugins\Plugin::getName() + */ + public static function getName() { + return self::PLUGIN_NAME; + } + + /** + * @see \ManiaControl\Plugins\Plugin::getVersion() + */ + public static function getVersion() { + return self::PLUGIN_VERSION; + } + + /** + * @see \ManiaControl\Plugins\Plugin::getAuthor() + */ + public static function getAuthor() { + return self::PLUGIN_AUTHOR; + } + + /** + * @see \ManiaControl\Plugins\Plugin::getDescription() + */ + public static function getDescription() { + return 'Integration of TMWT duo teams & pick and ban for MatchManager'; + } + + /** + * @param \ManiaControl\ManiaControl $maniaControl + * @return bool + * @see \ManiaControl\Plugins\Plugin::load() + */ + public function load(ManiaControl $maniaControl) { + $this->maniaControl = $maniaControl; + /** @var MatchManagerCore */ + $this->MatchManagerCore = $this->maniaControl->getPluginManager()->getPlugin(self::MATCHMANAGERCORE_PLUGIN); + + if ($this->MatchManagerCore == Null) { + throw new \Exception('MatchManager Core is needed to use ' . self::PLUGIN_NAME); + } + + // Settings + $this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_TEAM1, '', '', 10); + $this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_TEAM2, '', '', 10); + $this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_PICKANDBAN_ENABLE, false, '', 20); + $this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_PICKANDBAN_STEPCONFIG, '', 'Similar syntax as the ofiicial Competition Tool. e.g: b:1,b:0,p:0,p:1,p:1,p:0,b:0,b:1,p:r'); + $this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_PICKANDBAN_STEPDURATION, 60000, 'Each step duration in ms', 110); + $this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_PICKANDBAN_RESULTDURATION, 10000, 'result duration in ms', 120); + + // Callbacks + $this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ML_ACTION_OPENSETTINGS, $this, 'handleActionOpenSettings'); + $this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::AFTERINIT, $this, 'handleAfterInit'); + $this->maniaControl->getCallbackManager()->registerCallbackListener(PluginManager::CB_PLUGIN_LOADED, $this, 'handlePluginLoaded'); + $this->maniaControl->getCallbackManager()->registerCallbackListener($this->MatchManagerCore::CB_MATCHMANAGER_STARTMATCH, $this, 'handleMatchManagerStartMatch'); + $this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::MP_STARTMATCHSTART, $this, 'handleStartMatchStartCallback'); + $this->maniaControl->getCallbackManager()->registerScriptCallbackListener(self::XMLRPC_CALLBACK_STARTMAP, $this, 'handleStartMapStartCallback'); + $this->maniaControl->getCallbackManager()->registerCallbackListener($this->MatchManagerCore::CB_MATCHMANAGER_STOPMATCH, $this, 'handleMatchManagerEndMatch'); + $this->maniaControl->getCallbackManager()->registerCallbackListener($this->MatchManagerCore::CB_MATCHMANAGER_ENDMATCH, $this, 'handleMatchManagerEndMatch'); + $this->maniaControl->getCallbackManager()->registerScriptCallbackListener(self::XMLRPC_CALLBACK_PICKANDBANCOMPLETE, $this, 'handlePickAndBanComplete'); + $this->maniaControl->getCallbackManager()->registerCallbackListener(SettingManager::CB_SETTING_CHANGED, $this, 'handleSettingChanged'); + + $this->MatchManagerCore->addCanStartFunction($this, 'canStartMatch'); + $this->updateAdminUIMenuItems(); + } + + /** + * handle Plugin Loaded + * + * @param string $pluginClass + */ + public function handleAfterInit() { + $this->updateAdminUIMenuItems(); + } + + /** + * handle Plugin Loaded + * + * @param string $pluginClass + */ + public function handlePluginLoaded(string $pluginClass) { + if ($pluginClass === self::MATCHMANAGERADMINUI_PLUGIN) { + $this->updateAdminUIMenuItems(); + } + } + + /** + * Add items in AdminUI plugin + */ + public function updateAdminUIMenuItems() { + /** @var \MatchManagerSuite\MatchManagerAdminUI|null */ + $adminUIPlugin = $this->maniaControl->getPluginManager()->getPlugin(self::MATCHMANAGERADMINUI_PLUGIN); + if ($adminUIPlugin === null) return; + + $adminUIPlugin->removeMenuItem(self::ML_ACTION_OPENSETTINGS); + + $menuItem = new \MatchManagerSuite\MatchManagerAdminUI_MenuItem(); + $menuItem->setActionId(self::ML_ACTION_OPENSETTINGS) + ->setOrder(50) + ->setImageUrl('https://files.virtit.fr/TrackMania/Images/Others/TMWT_Logo.dds') + ->setSize(4.5) + ->setDescription('Open TMWT Integration Settings'); + $adminUIPlugin->addMenuItem($menuItem); + } + + /** + * handle Open settings manialink action + * + * @param array $callback + * @param Player $player + */ + public function handleActionOpenSettings(array $callback, Player $player) { + if ($player->authLevel <= 0) return; + + $pluginMenu = $this->maniaControl->getPluginManager()->getPluginMenu(); + if (defined("\ManiaControl\ManiaControl::ISTRACKMANIACONTROL")) { + $player->setCache($pluginMenu, PluginMenu::CACHE_SETTING_CLASS, "PluginMenu.Settings." . self::class); + } else { + $player->setCache($pluginMenu, PluginMenu::CACHE_SETTING_CLASS, self::class); + } + $this->maniaControl->getConfigurator()->showMenu($player, $pluginMenu); + } + + /** + * Callback function to check if everything is ok before starting the match + * + * @return bool + */ + public function canStartMatch() { + if ($this->maniaControl->getSettingManager()->getSettingValue($this->MatchManagerCore, 'S_TeamsUrl') === '') { + $this->maniaControl->getChat()->sendErrorToAdmins($this->MatchManagerCore->getChatPrefix() . " S_TeamsUrl must be defined"); + return false; + } + if ( + $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_TEAM1) === '' || + $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_TEAM2) === '' + ) { + $this->maniaControl->getChat()->sendErrorToAdmins($this->MatchManagerCore->getChatPrefix() . " Team Id must be defined"); + return false; + } + + if ($this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_PICKANDBAN_ENABLE)) { + if ($this->maniaControl->getSettingManager()->getSettingValue($this->MatchManagerCore, $this->MatchManagerCore::SETTING_MATCH_SETTINGS_MODE) !== "All from the plugin") { + $this->maniaControl->getChat()->sendErrorToAdmins('TMWT Pick and bans are only supported in Match Manager Core "All from the plugin" mode'); + return false; + } + } + + return true; + } + + /** + * handle MatchManagerCore StartMatch callback + * + * @return void + */ + public function handleMatchManagerStartMatch() { + $this->state = self::STATE_PRESETTING; + + $setting = $this->maniaControl->getSettingManager()->getSettingObject($this->MatchManagerCore, 'S_IsMatchmaking'); + if ($setting !== null && !$setting->value) { + $setting->value = true; + $this->maniaControl->getSettingManager()->saveSetting($setting); + Logger::logWarning('Remplacing S_IsMatchmaking setting value in MatchManagerCore for TMWT integration'); + $this->maniaControl->getChat()->sendErrorToAdmins('Remplacing S_IsMatchmaking setting value in MatchManagerCore for TMWT integration'); + } + + if ($this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_PICKANDBAN_ENABLE)) { + $setting = $this->maniaControl->getSettingManager()->getSettingObject($this->MatchManagerCore, 'S_PickAndBan_Enable'); + if ($setting !== null && !$setting->value) { + $setting->value = true; + $this->maniaControl->getSettingManager()->saveSetting($setting); + Logger::logWarning('Remplacing S_PickAndBan_Enable setting value in MatchManagerCore for TMWT integration'); + $this->maniaControl->getChat()->sendErrorToAdmins('Remplacing S_PickAndBan_Enable setting value in MatchManagerCore for TMWT integration'); + } + } + } + + /** + * handle MatchManagerCore StopMatch & EndMatch callback + * + * @return void + */ + public function handleMatchManagerEndMatch() { + $this->state = self::STATE_NOTHING; + } + + /** + * handle StartMatch_Start script callback + * + * @return void + */ + public function handleStartMatchStartCallback() { + if ($this->MatchManagerCore->getMatchStatus() && $this->state === self::STATE_PREMATCH) { + // reset match state in just in case + $this->maniaControl->getClient()->triggerModeScriptEvent(self::XMLRPC_METHOD_MATCHCOMPLETED, []); + } + } + + /** + * handle StartMap_Start script callback + * + * @return void + */ + public function handleStartMapStartCallback() { + if (!$this->MatchManagerCore->getMatchStatus()) return; + + if ($this->state === self::STATE_PRESETTING) { + $this->state = self::STATE_PREMATCH; + $this->maniaControl->getClient()->setModeScriptSettings(['S_IsMatchmaking' => true], false); + $this->maniaControl->getClient()->restartMap(); + } else if ($this->state === self::STATE_PREMATCH) { + + $teamsUrl = $this->maniaControl->getSettingManager()->getSettingValue($this->MatchManagerCore, 'S_TeamsUrl'); + if ($teamsUrl !== '') { + $response = WebReader::getUrl($teamsUrl); + $content = $response->getContent(); + $json = json_decode($content); + if ($json !== null) { + $team1 = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_TEAM1); + $team2 = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_TEAM2); + + foreach ($json as $team) { + if ($team->Id === $team1) { + $team1 = null; + foreach ($team->Players as $player) { + Logger::log($player->AccountId ." added to team 1"); + $this->maniaControl->getClient()->triggerModeScriptEvent(self::XMLRPC_METHOD_ADDPLAYER, [$player->AccountId, "1"], true); + } + } + if ($team->Id === $team2) { + $team2 = null; + foreach ($team->Players as $player) { + Logger::log($player->AccountId ." added to team 2"); + $this->maniaControl->getClient()->triggerModeScriptEvent(self::XMLRPC_METHOD_ADDPLAYER, [$player->AccountId, "2"], true); + } + } + + if ($team1 === null && $team2 === null) break; + } + + if ($this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_PICKANDBAN_ENABLE)) { + Logger::log('Starting Pick & ban in 10 seconds'); + $this->maniaControl->getChat()->sendSuccess('Starting pick & ban in 10 seconds'); + $this->maniaControl->getTimerManager()->registerOneTimeListening($this, function () { + $payload = [ + 'stepDuration' => $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_PICKANDBAN_STEPDURATION), + 'resultDuration' => $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_PICKANDBAN_RESULTDURATION), + 'steps' => [] + ]; + + $stepConfig = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_PICKANDBAN_STEPCONFIG); + foreach (explode(',', $stepConfig) as $stepRaw) { + $stepPayload = []; + $step = explode(':', $stepRaw); + + if ($step[1] === 'r') { + $stepPayload['action'] = 'randompick'; + } else { + if ($step[0] === 'p') { + $stepPayload['action'] = 'pick'; + } else if ($step[0] === 'b') { + $stepPayload['action'] = 'ban'; + } + + $stepPayload['team'] = $step[1] + 1; + } + + $payload['steps'][] = $stepPayload; + } + + $json = json_encode($payload); + + Logger::log('Starting Pick & ban: '. $json); + $this->maniaControl->getClient()->triggerModeScriptEvent(self::XMLRPC_METHOD_STARTPICKANDBAN, [$json], true); + }, 5000); + } else { + $this->state = self::STATE_MATCH; + Logger::log('Sending match start callback'); + $this->maniaControl->getClient()->triggerModeScriptEvent(self::XMLRPC_METHOD_MATCHSTARTED, [], true); + } + } + } + } + } + + /** + * handle Pick & Ban completed script callback + * + * @param array $structure + * @return void + */ + public function handlePickAndBanComplete(array $structure) { + if (!$this->MatchManagerCore->getMatchStatus()) return; + Logger::log('Received picks: '. $structure[1][0]); + + $this->maniaControl->getTimerManager()->registerOneTimeListening($this, function () use ($structure) { + try { + $json = json_decode($structure[1][0]); + + $mapUids = array_column($json->playlist, 'uid'); + $mapList = []; + foreach ($this->maniaControl->getMapManager()->getMaps() as $map) { + $index = array_search($map->uid, $mapUids); + + if ($index === false) { + $this->maniaControl->getClient()->removeMap($map->fileName); + } else { + $mapList[$index] = $map->fileName; + } + } + + if (count($mapUids) !== count($mapList)) { + Logger::logError("Missing maps: ". implode(' ', array_diff($mapUids, $mapList))); + } + + ksort($mapList); + + $this->maniaControl->getClient()->chooseNextMapList(array_values($mapList)); + } catch (\Throwable $th) { + Logger::logError("Can't apply map list: ". $th->getMessage()); + $this->maniaControl->getChat()->sendError("Can't apply map list: ". $th->getMessage()); + } + + $this->state = self::STATE_MATCH; + Logger::log('Sending match start callback'); + $this->maniaControl->getClient()->triggerModeScriptEvent(self::XMLRPC_METHOD_MATCHSTARTED, [], true); + $this->maniaControl->getMapManager()->getMapActions()->skipMap(); + }, $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_PICKANDBAN_RESULTDURATION)); + } + + /** + * handlePluginUnloaded + * + * @param string $pluginClass + * @param Plugin $plugin + * @return void + */ + public function handlePluginUnloaded(string $pluginClass, Plugin $plugin) { + if ($pluginClass == self::MATCHMANAGERCORE_PLUGIN) { + $this->maniaControl->getChat()->sendErrorToAdmins(self::PLUGIN_NAME . " disabled because MatchManager Core is now disabled"); + $this->maniaControl->getPluginManager()->deactivatePlugin((get_class())); + } + } + + /** + * handle Setting Changed callback + * + * @param Setting $setting + * @return void + */ + public function handleSettingChanged(Setting $setting) { + if ($setting->setting === self::SETTING_PICKANDBAN_ENABLE || $setting->setting === $this->MatchManagerCore::SETTING_MATCH_SETTINGS_MODE) { + if ($this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_PICKANDBAN_ENABLE)) { + if ($this->maniaControl->getSettingManager()->getSettingValue($this->MatchManagerCore, $this->MatchManagerCore::SETTING_MATCH_SETTINGS_MODE) !== "All from the plugin") { + $this->maniaControl->getChat()->sendErrorToAdmins('TMWT Pick and bans are only supported in Match Manager Core "All from the plugin" mode'); + } + } + } + } + + /** + * @see \ManiaControl\Plugins\Plugin::unload() + */ + public function unload() { + $this->MatchManagerCore->removeCanStartFunction($this, 'canStartMatch'); + /** @var \MatchManagerSuite\MatchManagerAdminUI|null */ + $adminUIPlugin = $this->maniaControl->getPluginManager()->getPlugin(self::MATCHMANAGERADMINUI_PLUGIN); + if ($adminUIPlugin !== null) { + $adminUIPlugin->removeMenuItem(self::ML_ACTION_OPENSETTINGS); + } + } +}