maniaControl->getChat()->sendErrorToAdmins('MatchManager Core is required to use MatchManagerPickAndBan plugin. Install it and restart Maniacontrol');
Logger::logError('MatchManager Core is required to use MatchManagerPickAndBan plugin. Install it and restart Maniacontrol');
return false;
}
/**
* MatchManager Pick and Ban
*
* @author Beu
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class MatchManagerPickAndBan implements ManialinkPageAnswerListener, CallbackListener, TimerListener, Plugin {
/*
* Constants
*/
const PLUGIN_ID = 223;
const PLUGIN_VERSION = 0.1;
const PLUGIN_NAME = 'MatchManager Pick & Ban';
const PLUGIN_AUTHOR = 'Beu';
const LOG_PREFIX = '[MatchManagerPickAndBan] ';
// Settings
const SETTING_ENABLE = 'Enable pick & ban';
const SETTING_STEPCONFIG = 'Step config';
const SETTING_STEPDURATION = 'Step duration';
// Other MatchManager plugin
const MATCHMANAGERCORE_PLUGIN = 'MatchManagerSuite\MatchManagerCore';
const MATCHMANAGERADMINUI_PLUGIN = 'MatchManagerSuite\MatchManagerAdminUI';
// Steps
const ML_STEP_OPENSETTINGS = 'MatchManagerSuite\MatchManagerPickAndBan.OpenSettings';
const ML_STEP_MAPCHOOSENREGEX = '/^MatchManagerPickAndBan_MapChoosen:.*/';
/*
* Private properties
*/
private ManiaControl $maniaControl;
private MatchManagerCore $MatchManagerCore;
private MatchManagerPickAndBan_State $state;
/**
* @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 'Pick & Ban plugin for MatchManager';
}
/**
* @param \ManiaControl\ManiaControl $maniaControl
* @return bool
* @see \ManiaControl\Plugins\Plugin::load()
*/
public function load(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
$this->clearManialinks();
$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);
}
$this->state = new MatchManagerPickAndBan_State;
// Settings
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_ENABLE, true, '', 0);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_STEPCONFIG, '', '', 0);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_STEPDURATION, 60, '', 0);
// Callbacks
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ML_STEP_OPENSETTINGS, $this, 'handleStepOpenSettings');
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerRegexListener(self::ML_STEP_MAPCHOOSENREGEX, $this, 'handleStepMapChoosen');
$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_ENDMATCH, $this, 'handleEndMatch');
$this->maniaControl->getCallbackManager()->registerCallbackListener($this->MatchManagerCore::CB_MATCHMANAGER_STOPMATCH, $this, 'handleEndMatch');
$this->MatchManagerCore->addCanStartFunction($this, 'handleCanStartMatch');
$this->updateAdminUIMenuItems();
$this->sendStateToPlayers();
}
/**
* @see \ManiaControl\Plugins\Plugin::unload()
*/
public function unload() {
/** @var \MatchManagerSuite\MatchManagerAdminUI|null */
$adminUIPlugin = $this->maniaControl->getPluginManager()->getPlugin(self::MATCHMANAGERADMINUI_PLUGIN);
if ($adminUIPlugin !== null) {
$adminUIPlugin->removeMenuItem(self::ML_STEP_OPENSETTINGS);
}
$this->clearManialinks();
}
/**
* Custom log function to add prefix
*
* @param mixed $message
*/
private function log(mixed $message) {
Logger::log(self::LOG_PREFIX . $message);
}
/**
* Custom logError function to add prefix
*
* @param mixed $message
*/
private function logError(mixed $message) {
Logger::logError(self::LOG_PREFIX . $message);
}
/**
* handle Plugin Loaded
*/
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_STEP_OPENSETTINGS);
$menuItem = new \MatchManagerSuite\MatchManagerAdminUI_MenuItem();
$menuItem->setActionId(self::ML_STEP_OPENSETTINGS)
->setOrder(40)
->setStyle('MeshModelerIcons')
->setSubStyle('LayersFocused')
->setDescription('Open Pick & Ban Settings');
$adminUIPlugin->addMenuItem($menuItem);
}
/**
* manage Step
*/
public function manageStep() {
// Do random pick
while (
array_key_exists($this->state->stepIndex, $this->state->steps) &&
$this->state->steps[$this->state->stepIndex]->login === ""
) {
$availableMaps = array_filter($this->state->maps, function($map) {
return !$map->picked && !$map->banned;
});
$mapUid = array_rand($availableMaps);
$this->state->maps[$mapUid]->banned = ($this->state->steps[$this->state->stepIndex]->type === MatchManagerPickAndBan_Step::STEP_BAN);
$this->state->maps[$mapUid]->picked = ($this->state->steps[$this->state->stepIndex]->type === MatchManagerPickAndBan_Step::STEP_PICK);
$this->state->steps[$this->state->stepIndex]->mapUid = $mapUid;
$this->state->stepIndex++;
}
$this->state->startTimestamp = time();
$this->state->endTimestamp = time() + $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_STEPDURATION);
$this->sendStateToPlayers();
}
/**
* handle timer
* @return void
*/
public function handleTimer() {
if ($this->state->status !== MatchManagerPickAndBan_State::STATUS_INPROGRESS) {
$this->maniaControl->getTimerManager()->unregisterTimerListening($this, 'handleTimer');
return;
}
if ($this->state->endTimestamp < time()) {
if (array_key_exists($this->state->stepIndex, $this->state->steps)) {
$this->state->steps[$this->state->stepIndex]->login = "";
$this->manageStep();
} else {
$this->log("Start match");
$this->state->status = MatchManagerPickAndBan_State::STATUS_COMPLETED;
$this->MatchManagerCore->MatchStart();
$this->clearManialinks();
}
}
}
/**
* handle Open settings manialink step
*
* @param array $callback
* @param Player $player
*/
public function handleStepOpenSettings(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);
}
/**
* handle Map Choosen
*
* @param array $callback
* @param Player $player
*/
public function handleStepMapChoosen(array $callback, Player $player) {
if ($this->state->status !== MatchManagerPickAndBan_State::STATUS_INPROGRESS) return;
if (!array_key_exists($this->state->stepIndex, $this->state->steps)) return;
if ($this->state->steps[$this->state->stepIndex]->login !== $player->login) return;
$step = $callback[1][2];
$stepArray = explode(':', $step);
if (count($stepArray) !== 2) return;
$mapUid = $stepArray[1];
if (!array_key_exists($mapUid, $this->state->maps)) return;
$this->state->maps[$mapUid]->banned = ($this->state->steps[$this->state->stepIndex]->type === MatchManagerPickAndBan_Step::STEP_BAN);
$this->state->maps[$mapUid]->picked = ($this->state->steps[$this->state->stepIndex]->type === MatchManagerPickAndBan_Step::STEP_PICK);
$this->state->steps[$this->state->stepIndex]->mapUid = $mapUid;
$this->state->stepIndex++;
$this->manageStep();
}
/**
* handle MatchManagerCore Can Start Match callback
*/
public function handleCanStartMatch() {
if (!$this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_ENABLE)) return true;
if ($this->state->status === MatchManagerPickAndBan_State::STATUS_COMPLETED) {
$maplist = [];
foreach ($this->state->steps as $step) {
if ($step->type !== MatchManagerPickAndBan_Step::STEP_PICK) continue;
if (!array_key_exists($step->mapUid, $this->state->maps)) {
$this->logError("Picked map doesn't exist in map list, should never happen.");
$this->maniaControl->getChat()->sendErrorToAdmins($this->MatchManagerCore->getChatPrefix() . "Picked map doesn't exist in map list, should never happen.");
$this->state = new MatchManagerPickAndBan_State;
return false;
}
$maplist[] = $this->state->maps[$step->mapUid]->filePath;
}
$setting = $this->maniaControl->getSettingManager()->getSettingObject($this->MatchManagerCore, $this->MatchManagerCore::SETTING_MODE_MAPS);
$setting->value = implode(',', $maplist);
$this->log('Starting match with maps "'. $setting->value .'"');
return true;
}
if ($this->state->status === MatchManagerPickAndBan_State::STATUS_INPROGRESS) {
$this->maniaControl->getChat()->sendErrorToAdmins($this->MatchManagerCore->getChatPrefix() . "Pick and Ban in progress");
return false;
}
$stepConfig = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_STEPCONFIG);
if ($stepConfig === '') {
$this->maniaControl->getChat()->sendErrorToAdmins($this->MatchManagerCore->getChatPrefix() . "'Step config' setting must be set");
return false;
}
if ($this->maniaControl->getSettingManager()->getSettingValue($this->MatchManagerCore, $this->MatchManagerCore::SETTING_MATCH_SETTINGS_MODE) !== 'All from the plugin') {
$this->maniaControl->getChat()->sendErrorToAdmins($this->MatchManagerCore->getChatPrefix() . "Setting mode must be in 'All from the plugin' to use Pick & Ban");
return false;
}
$mapFiles = $this->maniaControl->getSettingManager()->getSettingValue($this->MatchManagerCore, $this->MatchManagerCore::SETTING_MODE_MAPS);
if ($mapFiles === '') {
$this->maniaControl->getChat()->sendErrorToAdmins($this->MatchManagerCore->getChatPrefix() . "Maps must be defined to use Pick & Ban");
return false;
}
if (count(explode(',', $mapFiles)) < count(explode(',', $stepConfig))) {
$this->maniaControl->getChat()->sendErrorToAdmins($this->MatchManagerCore->getChatPrefix() . "You must have at least same number of maps than steps");
return false;
}
$this->log('Building Pick & Ban config');
$this->state = new MatchManagerPickAndBan_State;
$this->state->status = MatchManagerPickAndBan_State::STATUS_INPROGRESS;
$this->state->originalMapList = $mapFiles;
// build map config
$maps = [];
foreach (explode(',', $mapFiles) as $mapPath) {
try {
$mapInfo = $this->maniaControl->getClient()->getMapInfo($mapPath);
$mapObject = new MatchManagerPickAndBan_Map();
$mapObject->mapUid = $mapInfo->uId;
$mapObject->filePath = $mapPath;
$mapObject->name = $mapInfo->name;
$maps[$mapInfo->uId] = $mapObject;
} catch (Exception $e) {
$this->maniaControl->getChat()->sendErrorToAdmins($this->MatchManagerCore->getChatPrefix() . 'Error with the Map to play "' . $mapPath . '": ' . $e->getMessage());
return false;
}
}
uasort($maps, function ($a, $b) {
return strnatcasecmp($a->name, $b->name);
});
$this->state->maps = $maps;
$this->log(count($this->state->maps) . ' maps loaded for Pick & Ban');
// build steps
$steps = [];
foreach (explode(',', $stepConfig) as $index => $stepRaw) {
$stepArray = explode(':', $stepRaw);
if (count($stepArray) !== 2) {
$this->maniaControl->getChat()->sendErrorToAdmins($this->MatchManagerCore->getChatPrefix() . 'Invalid step ' . $index . 'config: ' . $stepRaw);
return false;
}
$step = new MatchManagerPickAndBan_Step;
if ($stepArray[0] === 'p') {
$step->type = MatchManagerPickAndBan_Step::STEP_PICK;
} else if ($stepArray[0] === 'b') {
$step->type = MatchManagerPickAndBan_Step::STEP_BAN;
}
if ($stepArray[1] === 'r') {
$step->login = "";
} else {
$step->login = $stepArray[1];
}
$steps[] = $step;
}
$this->state->steps = $steps;
$this->log(count($this->state->steps) . ' steps loaded for Pick & Ban');
$this->maniaControl->getChat()->sendSuccess('Starting Pick & Ban phase');
$this->log('Starting Pick & Ban phase');
$this->sendManialink();
$this->manageStep();
$this->maniaControl->getTimerManager()->registerTimerListening($this, 'handleTimer', 1000);
return false;
}
public function handleEndMatch() {
if ($this->state->status === MatchManagerPickAndBan_State::STATUS_COMPLETED) {
$setting = $this->maniaControl->getSettingManager()->getSettingObject($this->MatchManagerCore, $this->MatchManagerCore::SETTING_MODE_MAPS);
$setting->value = $this->state->originalMapList;
$this->state = new MatchManagerPickAndBan_State;
}
}
private function sendManialink(): void {
$manialink = '
';
$this->maniaControl->getManialinkManager()->sendManialink($manialink);
}
private function sendStateToPlayers(): void {
$manialink = '
';
$this->maniaControl->getManialinkManager()->sendManialink($manialink);
}
private function clearManialinks() {
$this->maniaControl->getManialinkManager()->hideManialink(self::getShortClassName() . ':UI');
$this->maniaControl->getManialinkManager()->hideManialink(self::getShortClassName() . ':State');
}
private function getManialinkStructures(): string {
return '
#Struct K_MatchManagerPickAndBan_Step {
Integer type;
Text login;
Text mapUid;
}
#Struct K_MatchManagerPickAndBan_Map {
Text mapUid;
Text name;
Boolean picked;
Boolean banned;
}
#Struct K_MatchManagerPickAndBan_State {
Integer status;
Integer stepIndex;
Integer startTimestamp;
Integer endTimestamp;
K_MatchManagerPickAndBan_Map[Text] maps;
K_MatchManagerPickAndBan_Step[] steps;
}
';
}
private static function getShortClassName(): string {
return (new \ReflectionClass(static::class))->getShortName();
}
}
class MatchManagerPickAndBan_State {
// States
const STATUS_WAITING = 0;
const STATUS_INPROGRESS = 1;
const STATUS_COMPLETED = 2;
public int $status = self::STATUS_WAITING;
public string $originalMapList = '';
public int $stepIndex = 0;
public int $startTimestamp;
public int $endTimestamp;
public array $steps = [];
public array $maps = [];
}
class MatchManagerPickAndBan_Step {
// Steps
const STEP_PICK = 1;
const STEP_BAN = 2;
public int $type;
public string $login;
public string $mapUid;
}
class MatchManagerPickAndBan_Map {
public string $mapUid;
public string $filePath;
public string $name;
public bool $picked = false;
public bool $banned = false;
}