maniaControl->getChat()->sendErrorToAdmins('MatchManager Core is required to use MatchManagerECircuitMania plugin. Install it and restart Maniacontrol'); Logger::logError('MatchManager Core is required to use MatchManagerECircuitMania plugin. Install it and restart Maniacontrol'); return false; } /** * MatchManagerECircuitMania * * @author Beu * @license http://www.gnu.org/licenses/ GNU General Public License, Version 3 */ class MatchManagerECircuitMania implements CallbackListener, ManialinkPageAnswerListener, TimerListener, Plugin { /* * Constants */ const PLUGIN_ID = 213; const PLUGIN_VERSION = 1.0; const PLUGIN_NAME = 'MatchManager eCircuitMania'; const PLUGIN_AUTHOR = 'Beu'; const MATCHMANAGERCORE_PLUGIN = 'MatchManagerSuite\MatchManagerCore'; const MATCHMANAGERADMINUI_PLUGIN = 'MatchManagerSuite\MatchManagerAdminUI'; const ML_ACTION_OPENSETTINGS = 'MatchManagerSuite\MatchManagerECircuitMania.OpenSettings'; const SETTING_URL = 'API URL'; const SETTING_MATCH_API_KEY = 'Match API Key'; const SETTING_WITHMATCHMANAGER = 'Only send data when a Match Manager match is running'; const CB_STARTMAP = 'Maniaplanet.StartMap_Start'; /* * Private properties */ private ManiaControl $maniaControl; private \MatchManagerSuite\MatchManagerCore $MatchManagerCore; private int $trackNum = 0; private int $roundNum = 0; /** * @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 "Plugin to send match data to eCM"; } /** * @see \ManiaControl\Plugins\Plugin::load() */ public function load(ManiaControl $maniaControl) { $this->maniaControl = $maniaControl; if ($this->maniaControl->getPluginManager()->getSavedPluginStatus(self::MATCHMANAGERCORE_PLUGIN)) { $this->maniaControl->getTimerManager()->registerOneTimeListening($this, function () { $this->MatchManagerCore = $this->maniaControl->getPluginManager()->getPlugin(self::MATCHMANAGERCORE_PLUGIN); if ($this->MatchManagerCore === null) { $this->maniaControl->getChat()->sendErrorToAdmins('MatchManager Core is needed to use ' . self::PLUGIN_NAME . ' plugin.'); $this->maniaControl->getPluginManager()->deactivatePlugin((get_class())); return; } $this->maniaControl->getCallbackManager()->registerScriptCallbackListener(self::CB_STARTMAP, $this, 'handleStartMap'); $this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::MP_STARTROUNDSTART, $this, 'handleStartRound'); $this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::TM_ONWAYPOINT, $this, 'handleOnWaypoint'); $this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::TM_SCORES, $this, 'handleTrackmaniaScores'); $this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::MP_ENDROUNDEND, $this, 'handleEndRound'); $this->updateAdminUIMenuItems(); }, 1); } else { throw new \Exception('MatchManager Core is needed to use ' . self::PLUGIN_NAME); } $this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_MATCH_API_KEY, "", "", 5); $this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_URL, "https://us-central1-fantasy-trackmania.cloudfunctions.net", "", 10); $this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ML_ACTION_OPENSETTINGS, $this, 'handleActionOpenSettings'); } /** * 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(60) ->setImageUrl('https://ecircuitmania.com/favicon.png') ->setSize(4.) ->setDescription('Open eCM 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); } public function handleStartMap(array $structure) { if (!$this->MatchManagerCore->getMatchIsRunning()) return; $data = json_decode($structure[1][0]); $this->trackNum = $data->valid; $this->roundNum = 0; } public function handleStartRound(StartEndStructure $structure) { if (!$this->MatchManagerCore->getMatchIsRunning()) return; $this->roundNum = $structure->getValidRoundCount(); } public function handleOnWaypoint(OnWayPointEventStructure $structure) { if (!$this->MatchManagerCore->getMatchIsRunning()) return; if (!$structure->getIsEndRace()) return; if ($this->roundNum <= 0) return; // probably during the WU $mapuid = ""; $map = $this->maniaControl->getMapManager()->getCurrentMap(); if ($map !== null) { $mapuid = $map->uid; } $payload = json_encode([ "ubisoftUid" => $structure->getPlayer()->getAccountId(), "finishTime" => $structure->getRaceTime(), "mapId" => $mapuid, "trackNum" => $this->trackNum, "roundNum" => $this->roundNum ]); $request = $this->getAPIRequest("/match-addRoundTime"); if ($request !== null) { $request->setContent($payload)->setCallable(function ($content, $error, $headers) use ($payload) { if ($content !== "Created" || $error !== null) { Logger::logWarning("Error on the 'addRoundTime' request. answer: " . $content . " / error: " . $error . " / payload: " . $payload); } })->postData(); } } public function handleTrackmaniaScores(OnScoresStructure $structure) { if (!$this->MatchManagerCore->getMatchIsRunning()) return; if ($structure->getSection() !== "PreEndRound") return; $scores = []; foreach ($structure->getPlayerScores() as $playerscore) { if ($playerscore->getMatchPoints() <= -2000) continue; $scores[] = $playerscore; } /** @var \ManiaControl\Callbacks\Structures\TrackMania\Models\PlayerScore[] $scores */ usort($scores, function ($a, $b) { if ($a->getPrevRaceTime() === -1 && $b->getPrevRaceTime() === -1) { return $b->getRoundPoints() - $a->getRoundPoints(); } if ($a->getPrevRaceTime() === -1) return 1; if ($b->getPrevRaceTime() === -1) return -1; if ($a->getPrevRaceTime() === $b->getPrevRaceTime()) { $acheckpoints = $a->getPrevRaceCheckpoints(); $bcheckpoints = $b->getPrevRaceCheckpoints(); while (end($acheckpoints) === end($bcheckpoints)) { if (count($acheckpoints) === 0 || count($bcheckpoints) === 0) return 0; array_pop($acheckpoints); array_pop($bcheckpoints); } return end($acheckpoints) - end($bcheckpoints); } return $a->getPrevRaceTime() - $b->getPrevRaceTime(); }); $players = []; $rank = 1; foreach ($scores as $playerscore) { /** @var \ManiaControl\Callbacks\Structures\TrackMania\Models\PlayerScore $playerscore */ if ($playerscore->getPlayer()->isSpectator) continue; $players[] = [ "ubisoftUid" => $playerscore->getPlayer()->getAccountId(), "finishTime" => $playerscore->getPrevRaceTime(), "position" => $rank ]; $rank++; } $mapuid = ""; $map = $this->maniaControl->getMapManager()->getCurrentMap(); if ($map !== null) { $mapuid = $map->uid; } $payload = json_encode([ "players" => $players, "mapId" => $mapuid, "trackNum" => $this->trackNum, "roundNum" => $this->roundNum ]); $request = $this->getAPIRequest("/match-addRound"); if ($request !== null) { $request->setContent($payload)->setCallable(function ($content, $error, $headers) use ($payload) { if ($content !== "Created" || $error !== null) { Logger::logWarning("Error on the 'addRound' request. answer: " . $content . " / error: " . $error . " / payload: " . $payload); } })->postData(); } } public function handleEndRound(StartEndStructure $structure) { if (!$this->MatchManagerCore->getMatchIsRunning()) return; $json = $structure->getPlainJsonObject(); if (!property_exists($json, 'isvalid') || $json->isvalid) return; $payload = json_encode([ "trackNum" => $this->trackNum, "roundNum" => $this->roundNum ]); $request = $this->getAPIRequest("/match-removeRound"); if ($request !== null) { $request->setContent($payload)->setCallable(function ($content, $error, $headers) use ($payload) { if ($content !== "Created" || $error !== null) { Logger::logWarning("Error on the 'removeRound' request. answer: " . $content . " / error: " . $error . " / payload: " . $payload); } })->postData(); } } private function getAPIRequest(string $url) : ?AsyncHttpRequest { $baseurl = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_URL); $matchapikey = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_MATCH_API_KEY); $array = explode("_", $matchapikey); if (count($array) !== 2) return null; $matchid = $array[0]; $token = $array[1]; if ($baseurl === "") return null; if ($token === "") return null; if ($matchid === "") return null; $asyncHttpRequest = new AsyncHttpRequest($this->maniaControl, $baseurl . $url . "?matchId=" . $matchid); $asyncHttpRequest->setContentType("application/json"); $asyncHttpRequest->setHeaders(["Authorization: " . $token]); return $asyncHttpRequest; } /** * Unload the plugin and its Resources */ public function unload() {} }