removed 'application' folder to have everything in the root directory

This commit is contained in:
Steffen Schröder
2014-09-29 18:20:09 +02:00
parent 1569fd5488
commit 9642433363
284 changed files with 4 additions and 50 deletions

318
core/Admin/ActionsMenu.php Normal file
View File

@ -0,0 +1,318 @@
<?php
namespace ManiaControl\Admin;
use FML\Controls\Control;
use FML\Controls\Frame;
use FML\Controls\Label;
use FML\Controls\Quad;
use FML\Controls\Quads\Quad_Icons64x64_1;
use FML\ManiaLink;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\Callbacks;
use ManiaControl\ManiaControl;
use ManiaControl\Manialinks\ManialinkPageAnswerListener;
use ManiaControl\Players\Player;
use ManiaControl\Players\PlayerManager;
/**
* Class managing Actions Menus
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class ActionsMenu implements CallbackListener, ManialinkPageAnswerListener {
/*
* Constants
*/
const MLID_MENU = 'ActionsMenu.MLID';
const SETTING_MENU_POSX = 'Menu Position: X';
const SETTING_MENU_POSY = 'Menu Position: Y';
const SETTING_MENU_ITEMSIZE = 'Menu Item Size';
const ACTION_OPEN_ADMIN_MENU = 'ActionsMenu.OpenAdminMenu';
const ACTION_OPEN_PLAYER_MENU = 'ActionsMenu.OpenPlayerMenu';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
private $adminMenuItems = array();
private $playerMenuItems = array();
private $initCompleted = false;
/**
* Construct a new Actions Menu instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Settings
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_MENU_POSX, 156.);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_MENU_POSY, -17.);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_MENU_ITEMSIZE, 6.);
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::AFTERINIT, $this, 'handleAfterInit');
$this->maniaControl->getCallbackManager()->registerCallbackListener(PlayerManager::CB_PLAYERCONNECT, $this, 'handlePlayerJoined');
$this->maniaControl->getCallbackManager()->registerCallbackListener(AuthenticationManager::CB_AUTH_LEVEL_CHANGED, $this, 'handlePlayerJoined');
}
/**
* Add a new Menu Item
*
* @param Control $control
* @param bool $playerAction
* @param int $order
* @param string $description
*/
public function addMenuItem(Control $control, $playerAction = true, $order = 0, $description = null) {
if ($playerAction) {
$this->addPlayerMenuItem($control, $order, $description);
} else {
$this->addAdminMenuItem($control, $order, $description);
}
}
/**
* Add a new Player Menu Item
*
* @param Control $control
* @param int $order
* @param string $description
*/
public function addPlayerMenuItem(Control $control, $order = 0, $description = null) {
if (!isset($this->playerMenuItems[$order])) {
$this->playerMenuItems[$order] = array();
}
array_push($this->playerMenuItems[$order], array($control, $description));
krsort($this->playerMenuItems);
$this->rebuildAndShowMenu();
}
/**
* Build and show the menus to everyone (if a menu get made after the init)
*/
public function rebuildAndShowMenu() {
if (!$this->initCompleted) {
return;
}
$players = $this->maniaControl->getPlayerManager()->getPlayers();
foreach ($players as $player) {
$manialink = $this->buildMenuIconsManialink($player);
$this->maniaControl->getManialinkManager()->sendManialink($manialink, $player->login);
}
}
/**
* Builds the Manialink
*
* @param Player $player
* @return ManiaLink
*/
private function buildMenuIconsManialink(Player $player) {
$posX = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_MENU_POSX);
$posY = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_MENU_POSY);
$itemSize = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_MENU_ITEMSIZE);
$shootManiaOffset = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultIconOffsetSM();
$quadStyle = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultQuadStyle();
$quadSubstyle = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultQuadSubstyle();
$itemMarginFactorX = 1.3;
$itemMarginFactorY = 1.2;
// If game is shootmania lower the icons position by 20
if ($this->maniaControl->getMapManager()->getCurrentMap()->getGame() === 'sm'
) {
$posY -= $shootManiaOffset;
}
$manialink = new ManiaLink(self::MLID_MENU);
/*
* Admin Menu
*/
if ($this->maniaControl->getAuthenticationManager()->checkRight($player, AuthenticationManager::AUTH_LEVEL_MODERATOR)
) {
// Admin Menu Icon Frame
$iconFrame = new Frame();
$manialink->add($iconFrame);
$iconFrame->setPosition($posX, $posY);
$backgroundQuad = new Quad();
$iconFrame->add($backgroundQuad);
$backgroundQuad->setSize($itemSize * $itemMarginFactorX, $itemSize * $itemMarginFactorY);
$backgroundQuad->setStyles($quadStyle, $quadSubstyle);
$itemQuad = new Quad_Icons64x64_1();
$iconFrame->add($itemQuad);
$itemQuad->setSubStyle($itemQuad::SUBSTYLE_IconServers);
$itemQuad->setSize($itemSize, $itemSize);
// Admin Menu Description
$descriptionLabel = new Label();
$manialink->add($descriptionLabel);
$descriptionLabel->setPosition($posX - count($this->adminMenuItems) * $itemSize * 1.15 - 6, $posY);
$descriptionLabel->setAlign($descriptionLabel::RIGHT, $descriptionLabel::TOP);
$descriptionLabel->setSize(40, 4);
$descriptionLabel->setTextSize(1.4);
$descriptionLabel->setTextColor('fff');
// Admin Menu
$popoutFrame = new Frame();
$manialink->add($popoutFrame);
$popoutFrame->setPosition($posX - $itemSize * 0.5, $posY);
$popoutFrame->setHAlign($popoutFrame::RIGHT);
$popoutFrame->setSize(4 * $itemSize * $itemMarginFactorX, $itemSize * $itemMarginFactorY);
$popoutFrame->setVisible(false);
$backgroundQuad = new Quad();
$popoutFrame->add($backgroundQuad);
$backgroundQuad->setHAlign($backgroundQuad::RIGHT);
$backgroundQuad->setStyles($quadStyle, $quadSubstyle);
$backgroundQuad->setSize(count($this->adminMenuItems) * $itemSize * 1.15 + 2, $itemSize * $itemMarginFactorY);
$itemQuad->addToggleFeature($popoutFrame);
// Add items
$itemPosX = -1;
foreach ($this->adminMenuItems as $menuItems) {
foreach ($menuItems as $menuItem) {
$menuQuad = $menuItem[0];
/** @var Quad $menuQuad */
$popoutFrame->add($menuQuad);
$menuQuad->setSize($itemSize, $itemSize);
$menuQuad->setX($itemPosX);
$menuQuad->setHAlign($menuQuad::RIGHT);
$itemPosX -= $itemSize * 1.05;
if ($menuItem[1]) {
$menuQuad->removeScriptFeatures();
$description = '$s' . $menuItem[1];
$menuQuad->addTooltipLabelFeature($descriptionLabel, $description);
}
}
}
}
/*
* Player Menu
*/
// Player Menu Icon Frame
$iconFrame = new Frame();
$manialink->add($iconFrame);
$iconFrame->setPosition($posX, $posY - $itemSize * $itemMarginFactorY);
$backgroundQuad = new Quad();
$iconFrame->add($backgroundQuad);
$backgroundQuad->setSize($itemSize * $itemMarginFactorX, $itemSize * $itemMarginFactorY);
$backgroundQuad->setStyles($quadStyle, $quadSubstyle);
$itemQuad = new Quad_Icons64x64_1();
$iconFrame->add($itemQuad);
$itemQuad->setSubStyle($itemQuad::SUBSTYLE_IconPlayers);
$itemQuad->setSize($itemSize, $itemSize);
// Player Menu Description
$descriptionLabel = new Label();
$manialink->add($descriptionLabel);
$descriptionLabel->setPosition($posX - count($this->playerMenuItems) * $itemSize * 1.15 - 6, $posY - $itemSize * $itemMarginFactorY);
$descriptionLabel->setAlign($descriptionLabel::RIGHT, $descriptionLabel::TOP);
$descriptionLabel->setSize(40, 4);
$descriptionLabel->setTextSize(1.4);
$descriptionLabel->setTextColor('fff');
// Player Menu
$popoutFrame = new Frame();
$manialink->add($popoutFrame);
$popoutFrame->setPosition($posX - $itemSize * 0.5, $posY - $itemSize * $itemMarginFactorY);
$popoutFrame->setHAlign($popoutFrame::RIGHT);
$popoutFrame->setSize(4 * $itemSize * $itemMarginFactorX, $itemSize * $itemMarginFactorY);
$popoutFrame->setVisible(false);
$backgroundQuad = new Quad();
$popoutFrame->add($backgroundQuad);
$backgroundQuad->setHAlign($backgroundQuad::RIGHT);
$backgroundQuad->setStyles($quadStyle, $quadSubstyle);
$backgroundQuad->setSize(count($this->playerMenuItems) * $itemSize * 1.15 + 2, $itemSize * $itemMarginFactorY);
$itemQuad->addToggleFeature($popoutFrame);
// Add items
$itemPosX = -1;
foreach ($this->playerMenuItems as $menuItems) {
foreach ($menuItems as $menuItem) {
$menuQuad = $menuItem[0];
/** @var Quad $menuQuad */
$popoutFrame->add($menuQuad);
$menuQuad->setSize($itemSize, $itemSize);
$menuQuad->setX($itemPosX);
$menuQuad->setHAlign($menuQuad::RIGHT);
$itemPosX -= $itemSize * 1.05;
if ($menuItem[1]) {
$menuQuad->removeScriptFeatures();
$description = '$s' . $menuItem[1];
$menuQuad->addTooltipLabelFeature($descriptionLabel, $description);
}
}
}
return $manialink;
}
/**
* Add a new Admin Menu Item
*
* @param Control $control
* @param int $order
* @param string $description
*/
public function addAdminMenuItem(Control $control, $order = 0, $description = null) {
if (!isset($this->adminMenuItems[$order])) {
$this->adminMenuItems[$order] = array();
}
array_push($this->adminMenuItems[$order], array($control, $description));
krsort($this->adminMenuItems);
$this->rebuildAndShowMenu();
}
/**
* Removes a Menu Item
*
* @param int $order
* @param bool $playerAction
*/
public function removeMenuItem($order, $playerAction = true) {
if ($playerAction) {
if ($this->playerMenuItems[$order]) {
unset($this->playerMenuItems[$order]);
}
} else {
if ($this->playerMenuItems[$order]) {
unset($this->adminMenuItems[$order]);
}
}
$this->rebuildAndShowMenu();
}
/**
* Handle ManiaControl AfterInit callback
*/
public function handleAfterInit() {
$this->initCompleted = true;
$this->rebuildAndShowMenu();
}
/**
* Handle PlayerJoined callback
*
* @param Player $player
*/
public function handlePlayerJoined(Player $player) {
$maniaLink = $this->buildMenuIconsManialink($player);
$this->maniaControl->getManialinkManager()->sendManialink($maniaLink, $player);
}
}

257
core/Admin/AdminLists.php Normal file
View File

@ -0,0 +1,257 @@
<?php
namespace ManiaControl\Admin;
use FML\Controls\Frame;
use FML\Controls\Labels\Label_Button;
use FML\Controls\Labels\Label_Text;
use FML\Controls\Quads\Quad_BgRaceScore2;
use FML\Controls\Quads\Quad_BgsPlayerCard;
use FML\Controls\Quads\Quad_UIConstruction_Buttons;
use FML\ManiaLink;
use FML\Script\Features\Paging;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\CallbackManager;
use ManiaControl\ManiaControl;
use ManiaControl\Manialinks\ManialinkManager;
use ManiaControl\Manialinks\ManialinkPageAnswerListener;
use ManiaControl\Players\Player;
/**
* Widget Class listing Authorized Players
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class AdminLists implements ManialinkPageAnswerListener, CallbackListener {
/*
* Constants
*/
const ACTION_OPEN_ADMIN_LIST = 'AdminList.OpenAdminList';
const ACTION_REVOKE_RIGHTS = 'AdminList.RevokeRights';
const MAX_PLAYERS_PER_PAGE = 15;
/*
* Private Properties
*/
private $adminListShown = array();
/**
* Construct a new PlayerList instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_PLAYERMANIALINKPAGEANSWER, $this, 'handleManialinkPageAnswer');
$this->maniaControl->getCallbackManager()->registerCallbackListener(ManialinkManager::CB_MAIN_WINDOW_CLOSED, $this, 'closeWidget');
$this->maniaControl->getCallbackManager()->registerCallbackListener(ManialinkManager::CB_MAIN_WINDOW_OPENED, $this, 'handleWidgetOpened');
$this->maniaControl->getCallbackManager()->registerCallbackListener(AuthenticationManager::CB_AUTH_LEVEL_CHANGED, $this, 'updateWidget');
// Menu Entry AdminList
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ACTION_OPEN_ADMIN_LIST, $this, 'openAdminList');
$itemQuad = new Quad_UIConstruction_Buttons();
$itemQuad->setSubStyle($itemQuad::SUBSTYLE_Author);
$itemQuad->setAction(self::ACTION_OPEN_ADMIN_LIST);
$this->maniaControl->getActionsMenu()->addMenuItem($itemQuad, false, 50, 'Open AdminList');
}
/**
* Open Admin List Action
*
* @param array $callback
* @param Player $player
*/
public function openAdminList(array $callback, Player $player) {
$this->showAdminLists($player);
}
/**
* Show the Admin List
*
* @param Player $player
*/
public function showAdminLists(Player $player) {
$this->adminListShown[$player->login] = true;
$width = $this->maniaControl->getManialinkManager()->getStyleManager()->getListWidgetsWidth();
$height = $this->maniaControl->getManialinkManager()->getStyleManager()->getListWidgetsHeight();
// get Admins
$admins = $this->maniaControl->getAuthenticationManager()->getAdmins();
//Create ManiaLink
$maniaLink = new ManiaLink(ManialinkManager::MAIN_MLID);
$script = $maniaLink->getScript();
$paging = new Paging();
$script->addFeature($paging);
// Main frame
$frame = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultListFrame($script, $paging);
$maniaLink->add($frame);
// Start offsets
$posX = -$width / 2;
$posY = $height / 2;
//Predefine description Label
$descriptionLabel = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultDescriptionLabel();
$frame->add($descriptionLabel);
// Headline
$headFrame = new Frame();
$frame->add($headFrame);
$headFrame->setY($posY - 5);
$array = array('Id' => $posX + 5, 'Nickname' => $posX + 18, 'Login' => $posX + 70, 'Actions' => $posX + 120);
$this->maniaControl->getManialinkManager()->labelLine($headFrame, $array);
$index = 1;
$posY -= 10;
$pageFrame = null;
foreach ($admins as $admin) {
if ($index % self::MAX_PLAYERS_PER_PAGE === 1) {
$pageFrame = new Frame();
$frame->add($pageFrame);
$paging->addPage($pageFrame);
$posY = $height / 2 - 10;
}
$playerFrame = new Frame();
$pageFrame->add($playerFrame);
$playerFrame->setY($posY);
if ($index % 2 !== 0) {
$lineQuad = new Quad_BgsPlayerCard();
$playerFrame->add($lineQuad);
$lineQuad->setSize($width, 4);
$lineQuad->setSubStyle($lineQuad::SUBSTYLE_BgPlayerCardBig);
$lineQuad->setZ(0.001);
}
$array = array($index => $posX + 5, $admin->nickname => $posX + 18, $admin->login => $posX + 70);
$this->maniaControl->getManialinkManager()->labelLine($playerFrame, $array);
// Level Quad
$rightQuad = new Quad_BgRaceScore2();
$playerFrame->add($rightQuad);
$rightQuad->setX($posX + 13);
$rightQuad->setZ(5);
$rightQuad->setSubStyle($rightQuad::SUBSTYLE_CupFinisher);
$rightQuad->setSize(7, 3.5);
$rightLabel = new Label_Text();
$playerFrame->add($rightLabel);
$rightLabel->setX($posX + 13.9);
$rightLabel->setTextSize(0.8);
$rightLabel->setZ(10);
$rightLabel->setText($this->maniaControl->getAuthenticationManager()->getAuthLevelAbbreviation($admin));
$description = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($admin) . " " . $admin->nickname;
$rightLabel->addTooltipLabelFeature($descriptionLabel, $description);
//Revoke Button
if ($admin->authLevel > 0
&& $this->maniaControl->getAuthenticationManager()->checkRight($player, $admin->authLevel + 1)
) {
//Settings
$style = Label_Text::STYLE_TextCardSmall;
$textColor = 'FFF';
$quadWidth = 24;
$quadHeight = 3.4;
// Quad
$quad = new Quad_BgsPlayerCard();
$playerFrame->add($quad);
$quad->setZ(11);
$quad->setX($posX + 130);
$quad->setSubStyle($quad::SUBSTYLE_BgPlayerCardBig);
$quad->setSize($quadWidth, $quadHeight);
$quad->setAction(self::ACTION_REVOKE_RIGHTS . "." . $admin->login);
//Label
$label = new Label_Button();
$playerFrame->add($label);
$label->setX($posX + 130);
$quad->setZ(12);
$label->setStyle($style);
$label->setTextSize(1);
$label->setTextColor($textColor);
$label->setText("Revoke Rights");
}
$posY -= 4;
$index++;
}
// Render and display xml
$this->maniaControl->getManialinkManager()->displayWidget($maniaLink, $player, 'AdminList');
}
/**
* Called on ManialinkPageAnswer
*
* @param array $callback
*/
public function handleManialinkPageAnswer(array $callback) {
$actionId = $callback[1][2];
$actionArray = explode('.', $actionId, 3);
if (count($actionArray) <= 2) {
return;
}
$action = $actionArray[0] . '.' . $actionArray[1];
$adminLogin = $callback[1][1];
$targetLogin = $actionArray[2];
switch ($action) {
case self::ACTION_REVOKE_RIGHTS:
$this->maniaControl->getPlayerManager()->getPlayerActions()->revokeAuthLevel($adminLogin, $targetLogin);
break;
}
}
/**
* Reopen the widget on Map Begin, MapListChanged, etc.
*
* @param Player $player
*/
public function updateWidget(Player $player) {
foreach ($this->adminListShown as $login => $shown) {
if ($shown) {
$player = $this->maniaControl->getPlayerManager()->getPlayer($login);
if ($player) {
$this->showAdminLists($player);
} else {
unset($this->adminListShown[$login]);
}
}
}
}
/**
* Closes the widget
*
* @param \ManiaControl\Players\Player $player
*/
public function closeWidget(Player $player) {
unset($this->adminListShown[$player->login]);
}
/**
* Unset the player if he opened another Main Widget
*
* @param Player $player
* @param $openedWidget
*/
public function handleWidgetOpened(Player $player, $openedWidget) {
//unset when another main widget got opened
if ($openedWidget !== 'AdminList') {
unset($this->adminListShown[$player->login]);
}
}
}

162
core/Admin/AuthCommands.php Normal file
View File

@ -0,0 +1,162 @@
<?php
namespace ManiaControl\Admin;
use ManiaControl\Commands\CommandListener;
use ManiaControl\ManiaControl;
use ManiaControl\Players\Player;
/**
* Class offering Commands to grant Authorizations to Players
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class AuthCommands implements CommandListener {
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/**
* Construct a new AuthCommands instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Commands
$this->maniaControl->getCommandManager()->registerCommandListener('addsuperadmin', $this, 'command_AddSuperAdmin', true, 'Add Player to the AdminList as SuperAdmin.');
$this->maniaControl->getCommandManager()->registerCommandListener('addadmin', $this, 'command_AddAdmin', true, 'Add Player to the AdminList as Admin.');
$this->maniaControl->getCommandManager()->registerCommandListener('addmod', $this, 'command_AddModerator', true, 'Add Player to the AdminList as Moderator.');
}
/**
* Handle //addsuperadmin command
*
* @param array $chatCallback
* @param Player $player
*/
public function command_AddSuperAdmin(array $chatCallback, Player $player) {
if (!AuthenticationManager::checkRight($player, AuthenticationManager::AUTH_LEVEL_MASTERADMIN)) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$text = $chatCallback[1][2];
$commandParts = explode(' ', $text);
if (!array_key_exists(1, $commandParts)) {
$this->sendAddSuperAdminUsageInfo($player);
return;
}
$target = $this->maniaControl->getPlayerManager()->getPlayer($commandParts[1]);
if (!$target) {
$this->maniaControl->getChat()->sendError("Player '{$commandParts[1]}' not found!", $player);
return;
}
$success = $this->maniaControl->getAuthenticationManager()->grantAuthLevel($target, AuthenticationManager::AUTH_LEVEL_SUPERADMIN);
if (!$success) {
$this->maniaControl->getChat()->sendError('Error occurred.', $player);
return;
}
$message = $player->getEscapedNickname() . ' added ' . $target->getEscapedNickname() . ' as SuperAdmin!';
$this->maniaControl->getChat()->sendSuccess($message);
}
/**
* Send usage example for //addsuperadmin command
*
* @param Player $player
* @return bool
*/
private function sendAddSuperAdminUsageInfo(Player $player) {
$message = "Usage Example: '//addsuperadmin login'";
return $this->maniaControl->getChat()->sendUsageInfo($message, $player);
}
/**
* Handle //addadmin command
*
* @param array $chatCallback
* @param Player $player
*/
public function command_AddAdmin(array $chatCallback, Player $player) {
if (!AuthenticationManager::checkRight($player, AuthenticationManager::AUTH_LEVEL_SUPERADMIN)) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$text = $chatCallback[1][2];
$commandParts = explode(' ', $text);
if (!array_key_exists(1, $commandParts)) {
$this->sendAddAdminUsageInfo($player);
return;
}
$target = $this->maniaControl->getPlayerManager()->getPlayer($commandParts[1]);
if (!$target) {
$this->maniaControl->getChat()->sendError("Player '{$commandParts[1]}' not found!", $player);
return;
}
$success = $this->maniaControl->getAuthenticationManager()->grantAuthLevel($target, AuthenticationManager::AUTH_LEVEL_ADMIN);
if (!$success) {
$this->maniaControl->getChat()->sendError('Error occurred.', $player);
return;
}
$message = $player->getEscapedNickname() . ' added ' . $target->getEscapedNickname() . ' as Admin!';
$this->maniaControl->getChat()->sendSuccess($message);
}
/**
* Send usage example for //addadmin command
*
* @param Player $player
* @return bool
*/
private function sendAddAdminUsageInfo(Player $player) {
$message = "Usage Example: '//addadmin login'";
return $this->maniaControl->getChat()->sendUsageInfo($message, $player);
}
/**
* Handle //addmod command
*
* @param array $chatCallback
* @param Player $player
*/
public function command_AddModerator(array $chatCallback, Player $player) {
if (!AuthenticationManager::checkRight($player, AuthenticationManager::AUTH_LEVEL_ADMIN)) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$text = $chatCallback[1][2];
$commandParts = explode(' ', $text);
if (!array_key_exists(1, $commandParts)) {
$this->sendAddModeratorUsageInfo($player);
return;
}
$target = $this->maniaControl->getPlayerManager()->getPlayer($commandParts[1]);
if (!$target) {
$this->maniaControl->getChat()->sendError("Player '{$commandParts[1]}' not found!", $player);
return;
}
$success = $this->maniaControl->getAuthenticationManager()->grantAuthLevel($target, AuthenticationManager::AUTH_LEVEL_MODERATOR);
if (!$success) {
$this->maniaControl->getChat()->sendError('Error occurred.', $player);
return;
}
$message = $player->getEscapedNickname() . ' added ' . $target->getEscapedNickname() . ' as Moderator!';
$this->maniaControl->getChat()->sendSuccess($message);
}
/**
* Send usage example for //addmod command
*
* @param Player $player
* @return bool
*/
private function sendAddModeratorUsageInfo(Player $player) {
$message = "Usage Example: '//addmod login'";
return $this->maniaControl->getChat()->sendUsageInfo($message, $player);
}
}

View File

@ -0,0 +1,361 @@
<?php
namespace ManiaControl\Admin;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\Callbacks;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
use ManiaControl\Players\Player;
use ManiaControl\Players\PlayerManager;
use ManiaControl\Settings\Setting;
/**
* Class managing Authentication Levels
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class AuthenticationManager implements CallbackListener {
/*
* Constants
*/
const AUTH_LEVEL_PLAYER = 0;
const AUTH_LEVEL_MODERATOR = 1;
const AUTH_LEVEL_ADMIN = 2;
const AUTH_LEVEL_SUPERADMIN = 3;
const AUTH_LEVEL_MASTERADMIN = 4;
const AUTH_NAME_PLAYER = 'Player';
const AUTH_NAME_MODERATOR = 'Moderator';
const AUTH_NAME_ADMIN = 'Admin';
const AUTH_NAME_SUPERADMIN = 'SuperAdmin';
const AUTH_NAME_MASTERADMIN = 'MasterAdmin';
const CB_AUTH_LEVEL_CHANGED = 'AuthenticationManager.AuthLevelChanged';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/** @var AuthCommands $authCommands */
private $authCommands = null;
/**
* Construct a new Authentication Manager instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
$this->authCommands = new AuthCommands($maniaControl);
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::ONINIT, $this, 'handleOnInit');
}
/**
* Get Name of the Authentication Level from Level Int
*
* @param mixed $authLevelInt
* @return string
*/
public static function getAuthLevelName($authLevelInt) {
$authLevelInt = self::getAuthLevelInt($authLevelInt);
switch ($authLevelInt) {
case self::AUTH_LEVEL_MASTERADMIN:
return self::AUTH_NAME_MASTERADMIN;
case self::AUTH_LEVEL_SUPERADMIN:
return self::AUTH_NAME_SUPERADMIN;
case self::AUTH_LEVEL_ADMIN:
return self::AUTH_NAME_ADMIN;
case self::AUTH_LEVEL_MODERATOR:
return self::AUTH_NAME_MODERATOR;
}
return self::AUTH_NAME_PLAYER;
}
/**
* Get the Authentication Level Int from the given Param
*
* @param mixed $authLevelParam
* @return int
*/
public static function getAuthLevelInt($authLevelParam) {
if (is_object($authLevelParam) && property_exists($authLevelParam, 'authLevel')) {
return (int)$authLevelParam->authLevel;
}
if (is_string($authLevelParam)) {
return self::getAuthLevel($authLevelParam);
}
return (int)$authLevelParam;
}
/**
* Get Authentication Level Int from Level Name
*
* @param string $authLevelName
* @return int
*/
public static function getAuthLevel($authLevelName) {
$authLevelName = (string)$authLevelName;
switch ($authLevelName) {
case self::AUTH_NAME_MASTERADMIN:
return self::AUTH_LEVEL_MASTERADMIN;
case self::AUTH_NAME_SUPERADMIN:
return self::AUTH_LEVEL_SUPERADMIN;
case self::AUTH_NAME_ADMIN:
return self::AUTH_LEVEL_ADMIN;
case self::AUTH_NAME_MODERATOR:
return self::AUTH_LEVEL_MODERATOR;
}
return self::AUTH_LEVEL_PLAYER;
}
/**
* Get the Abbreviation of the Authentication Level from Level Int
*
* @param mixed $authLevelInt
* @return string
*/
public static function getAuthLevelAbbreviation($authLevelInt) {
$authLevelInt = self::getAuthLevelInt($authLevelInt);
switch ($authLevelInt) {
case self::AUTH_LEVEL_MASTERADMIN:
return 'MA';
case self::AUTH_LEVEL_SUPERADMIN:
return 'SA';
case self::AUTH_LEVEL_ADMIN:
return 'AD';
case self::AUTH_LEVEL_MODERATOR:
return 'MOD';
}
return '';
}
/**
* Handle ManiaControl OnInit Callback
*/
public function handleOnInit() {
$this->updateMasterAdmins();
}
/**
* Update MasterAdmins based on Config
*
* @return bool
*/
private function updateMasterAdmins() {
$masterAdminsElements = $this->maniaControl->getConfig()->xpath('masteradmins');
if (!$masterAdminsElements) {
Logger::logError('Missing MasterAdmins configuration!');
return false;
}
$masterAdminsElement = $masterAdminsElements[0];
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
// Remove all MasterAdmins
$adminQuery = "UPDATE `" . PlayerManager::TABLE_PLAYERS . "`
SET `authLevel` = ?
WHERE `authLevel` = ?;";
$adminStatement = $mysqli->prepare($adminQuery);
if ($mysqli->error) {
trigger_error($mysqli->error, E_USER_ERROR);
return false;
}
$adminLevel = self::AUTH_LEVEL_SUPERADMIN;
$masterAdminLevel = self::AUTH_LEVEL_MASTERADMIN;
$adminStatement->bind_param('ii', $adminLevel, $masterAdminLevel);
$adminStatement->execute();
if ($adminStatement->error) {
trigger_error($adminStatement->error);
}
$adminStatement->close();
// Set configured MasterAdmins
$loginElements = $masterAdminsElement->xpath('login');
$adminQuery = "INSERT INTO `" . PlayerManager::TABLE_PLAYERS . "` (
`login`,
`authLevel`
) VALUES (
?, ?
) ON DUPLICATE KEY UPDATE
`authLevel` = VALUES(`authLevel`);";
$adminStatement = $mysqli->prepare($adminQuery);
if ($mysqli->error) {
trigger_error($mysqli->error, E_USER_ERROR);
return false;
}
$success = true;
foreach ($loginElements as $loginElement) {
$login = (string)$loginElement;
$adminStatement->bind_param('si', $login, $masterAdminLevel);
$adminStatement->execute();
if ($adminStatement->error) {
trigger_error($adminStatement->error);
$success = false;
}
}
$adminStatement->close();
return $success;
}
/**
* Get all connected Players with at least the given Auth Level
*
* @param int $authLevel
* @return Player[]
*/
public function getConnectedAdmins($authLevel = self::AUTH_LEVEL_MODERATOR) {
$players = $this->maniaControl->getPlayerManager()->getPlayers();
$admins = array();
foreach ($players as $player) {
if (self::checkRight($player, $authLevel)) {
array_push($admins, $player);
}
}
return $admins;
}
/**
* Check whether the Player has enough Rights
*
* @param Player $player
* @param int|Setting $neededAuthLevel
* @return bool
*/
public static function checkRight(Player $player, $neededAuthLevel) {
if ($neededAuthLevel instanceof Setting) {
$neededAuthLevel = $neededAuthLevel->value;
}
return ($player->authLevel >= $neededAuthLevel);
}
/**
* Get a List of all Admins
*
* @param int $authLevel
* @return Player[]
*/
public function getAdmins($authLevel = self::AUTH_LEVEL_MODERATOR) {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$query = "SELECT `login` FROM `" . PlayerManager::TABLE_PLAYERS . "`
WHERE `authLevel` > " . $authLevel . "
ORDER BY `authLevel` DESC;";
$result = $mysqli->query($query);
if (!$result) {
trigger_error($mysqli->error);
return null;
}
$admins = array();
while ($row = $result->fetch_object()) {
$player = $this->maniaControl->getPlayerManager()->getPlayer($row->login, false);
if ($player) {
array_push($admins, $player);
}
}
$result->free();
return $admins;
}
/**
* Grant the Auth Level to the Player
*
* @param Player $player
* @param int $authLevel
* @return bool
*/
public function grantAuthLevel(Player &$player, $authLevel) {
if (!$player || !is_numeric($authLevel)) {
return false;
}
$authLevel = (int)$authLevel;
if ($authLevel >= self::AUTH_LEVEL_MASTERADMIN) {
return false;
}
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$authQuery = "INSERT INTO `" . PlayerManager::TABLE_PLAYERS . "` (
`login`,
`authLevel`
) VALUES (
?, ?
) ON DUPLICATE KEY UPDATE
`authLevel` = VALUES(`authLevel`);";
$authStatement = $mysqli->prepare($authQuery);
if ($mysqli->error) {
trigger_error($mysqli->error, E_USER_ERROR);
return false;
}
$authStatement->bind_param('si', $player->login, $authLevel);
$authStatement->execute();
if ($authStatement->error) {
trigger_error($authStatement->error);
$authStatement->close();
return false;
}
$authStatement->close();
$player->authLevel = $authLevel;
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_AUTH_LEVEL_CHANGED, $player);
return true;
}
/**
* Send an Error Message to the Player
*
* @param Player $player
* @return bool
*/
public function sendNotAllowed(Player $player) {
if (!$player) {
return false;
}
return $this->maniaControl->getChat()->sendError('You do not have the required Rights to perform this Action!', $player);
}
/**
* Checks the permission by a right name
*
* @param Player $player
* @param $rightName
* @return bool
*/
public function checkPermission(Player $player, $rightName) {
$right = $this->maniaControl->getSettingManager()->getSettingValue($this, $rightName);
return $this->checkRight($player, $this->getAuthLevel($right));
}
/**
* Define a Minimum Right Level needed for an Action
*
* @param string $rightName
* @param int $authLevelNeeded
*/
public function definePermissionLevel($rightName, $authLevelNeeded) {
$this->maniaControl->getSettingManager()->initSetting($this, $rightName, $this->getPermissionLevelNameArray($authLevelNeeded));
}
/**
* Get the PermissionLevelNameArray
*
* @param $authLevelNeeded
* @return array[]
*/
private function getPermissionLevelNameArray($authLevelNeeded) {
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);
}
return array("-");
}
}

47
core/AutoLoader.php Normal file
View File

@ -0,0 +1,47 @@
<?php
namespace ManiaControl;
/**
* ManiaControl AutoLoader
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
abstract class AutoLoader {
/**
* Register the Auto Loader
*/
public static function register() {
spl_autoload_register(array(get_class(), 'autoload'));
}
/**
* Try to autoload the Class with the given Name
*
* @param string $className
*/
public static function autoload($className) {
$classPath = str_replace('\\', DIRECTORY_SEPARATOR, $className);
// Core file
$coreClassPath = preg_replace('/ManiaControl/', 'core', $classPath, 1);
$coreFilePath = MANIACONTROL_PATH . $coreClassPath . '.php';
if (file_exists($coreFilePath)) {
include_once $coreFilePath;
return;
}
// Other file
$paths = array('plugins', 'libs', 'libs' . DIRECTORY_SEPARATOR . 'curl-easy');
foreach ($paths as $path) {
$filePath = MANIACONTROL_PATH . $path . DIRECTORY_SEPARATOR . $classPath . '.php';
if (file_exists($filePath)) {
include_once $filePath;
return;
}
}
}
}

42
core/Bills/BillData.php Normal file
View File

@ -0,0 +1,42 @@
<?php
namespace ManiaControl\Bills;
use ManiaControl\Players\Player;
/**
* ManiaControl BillData Structure
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class BillData {
/*
* Public properties
*/
public $function = null;
public $pay = false;
public $player = null;
public $receiverLogin = null;
public $amount = 0;
public $creationTime = -1;
/**
* Construct new Bill Data Model
*
* @param callable $function
* @param Player|string $player
* @param int $amount
* @param bool $pay
* @param string $receiverLogin
*/
public function __construct(callable $function, $player, $amount, $pay = false, $receiverLogin = null) {
$this->function = $function;
$this->player = $player;
$this->amount = $amount;
$this->pay = $pay;
$this->receiverLogin = $receiverLogin;
$this->creationTime = time();
}
}

115
core/Bills/BillManager.php Normal file
View File

@ -0,0 +1,115 @@
<?php
namespace ManiaControl\Bills;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\CallbackManager;
use ManiaControl\ManiaControl;
use ManiaControl\Players\Player;
use Maniaplanet\DedicatedServer\Structures\Bill;
/**
* ManiaControl Bill Manager Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class BillManager implements CallbackListener {
/*
* Constants
*/
const DONATED_TO_SERVER = 1;
const DONATED_TO_RECEIVER = 2;
const PAYED_FROM_SERVER = 3;
const PLAYER_REFUSED_DONATION = 4;
const ERROR_WHILE_TRANSACTION = 5;
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/** @var BillData[] $openBills */
private $openBills = array();
/**
* Construct a new Bill Manager instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_BILLUPDATED, $this, 'handleBillUpdated');
}
/**
* Send a bill to a player
*
* @param callable $function
* @param Player $player
* @param int $amount
* @param string $message
* @param string $receiver
* @return bool
*/
public function sendBill(callable $function, Player $player, $amount, $message, $receiver = '') {
$billId = $this->maniaControl->getClient()->sendBill($player->login, $amount, $message, $receiver);
$this->openBills[$billId] = new BillData($function, $player, $amount);
return true;
}
/**
* Send planets from the server to a player
*
* @param callable $function
* @param string $receiverLogin
* @param int $amount
* @param string $message
* @return bool
*/
public function sendPlanets(callable $function, $receiverLogin, $amount, $message) {
$billId = $this->maniaControl->getClient()->pay($receiverLogin, $amount, $message);
$this->openBills[$billId] = new BillData($function, $receiverLogin, $amount, true);
return true;
}
/**
* Handle bill updated callback
*
* @param array $callback
* @return bool
*/
public function handleBillUpdated(array $callback) {
$billId = $callback[1][0];
if (!isset($this->openBills[$billId])) {
return;
}
$billData = $this->openBills[$billId];
switch ($callback[1][1]) {
case Bill::STATE_PAYED:
if ($billData->pay) {
call_user_func($billData->function, $billData, self::PAYED_FROM_SERVER);
} else {
if ($billData->receiverLogin) {
call_user_func($billData->function, $billData, self::DONATED_TO_RECEIVER);
} else {
call_user_func($billData->function, $billData, self::DONATED_TO_SERVER);
}
}
unset($this->openBills[$billId]);
break;
case Bill::STATE_REFUSED:
call_user_func($billData->function, $billData, self::PLAYER_REFUSED_DONATION);
unset($this->openBills[$billId]);
break;
case Bill::STATE_ERROR:
call_user_func($billData->function, $callback[1][2], self::ERROR_WHILE_TRANSACTION);
unset($this->openBills[$billId]);
break;
}
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace ManiaControl\Callbacks;
/**
* Interface for Callback Listener
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
interface CallbackListener {
}

View File

@ -0,0 +1,319 @@
<?php
namespace ManiaControl\Callbacks;
use ManiaControl\Callbacks\Models\BaseCallback;
use ManiaControl\ManiaControl;
/**
* Class for managing Server and ManiaControl Callbacks
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class CallbackManager {
/*
* Constants
*/
// ManiaPlanet callbacks
const CB_MP_SERVERSTART = 'ManiaPlanet.ServerStart';
const CB_MP_SERVERSTOP = 'ManiaPlanet.ServerStop';
const CB_MP_BEGINMATCH = 'ManiaPlanet.BeginMatch';
const CB_MP_ENDMATCH = 'ManiaPlanet.EndMatch';
const CB_MP_BEGINMAP = 'ManiaPlanet.BeginMap';
const CB_MP_ENDMAP = 'ManiaPlanet.EndMap';
const CB_MP_MAPLISTMODIFIED = 'ManiaPlanet.MapListModified';
const CB_MP_ECHO = 'ManiaPlanet.Echo';
const CB_MP_BILLUPDATED = 'ManiaPlanet.BillUpdated';
const CB_MP_PLAYERCHAT = 'ManiaPlanet.PlayerChat';
const CB_MP_PLAYERCONNECT = 'ManiaPlanet.PlayerConnect';
const CB_MP_PLAYERDISCONNECT = 'ManiaPlanet.PlayerDisconnect';
const CB_MP_PLAYERMANIALINKPAGEANSWER = 'ManiaPlanet.PlayerManialinkPageAnswer';
const CB_MP_PLAYERINFOCHANGED = 'ManiaPlanet.PlayerInfoChanged';
const CB_MP_PLAYERALLIESCHANGED = 'ManiaPlanet.PlayerAlliesChanged';
const CB_MP_VOTEUPDATED = 'ManiaPlanet.VoteUpdated';
const CB_MP_STATUSCHANGED = 'ManiaPlanet.StatusChanged';
const CB_MP_MODESCRIPTCALLBACK = 'ManiaPlanet.ModeScriptCallback';
const CB_MP_MODESCRIPTCALLBACKARRAY = 'ManiaPlanet.ModeScriptCallbackArray';
const CB_MP_TUNNELDATARECEIVED = 'ManiaPlanet.TunnelDataReceived';
// TrackMania callbacks
const CB_TM_PLAYERCHECKPOINT = 'TrackMania.PlayerCheckpoint';
const CB_TM_PLAYERFINISH = 'TrackMania.PlayerFinish';
const CB_TM_PLAYERINCOHERENCE = 'TrackMania.PlayerIncoherence';
/*
* Public properties
*/
public $libXmlRpcCallbacks = null;
public $shootManiaCallbacks = null;
public $trackManiaCallbacks = null;
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/** @var Listening[][] $callbackListenings */
private $callbackListenings = array();
/** @var Listening[][] $scriptCallbackListenings */
private $scriptCallbackListenings = array();
/**
* Construct a new callbacks manager instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
$this->libXmlRpcCallbacks = new LibXmlRpcCallbacks($maniaControl, $this);
$this->shootManiaCallbacks = new ShootManiaCallbacks($maniaControl, $this);
$this->trackManiaCallbacks = new TrackManiaCallbacks($maniaControl, $this);
}
/**
* Register a new Callback Listener
*
* @param string $callbackName
* @param CallbackListener $listener
* @param string $method
* @return bool
*/
public function registerCallbackListener($callbackName, CallbackListener $listener, $method) {
if (is_array($callbackName)) {
$success = false;
foreach ($callbackName as $callback) {
if ($this->registerCallbackListener($callback, $listener, $method)) {
$success = true;
}
}
return $success;
}
if (!Listening::checkValidCallback($listener, $method)) {
$listenerClass = get_class($listener);
trigger_error("Given Listener '{$listenerClass}' can't handle Callback '{$callbackName}': No callable Method '{$method}'!");
return false;
}
if (!array_key_exists($callbackName, $this->callbackListenings)) {
$this->callbackListenings[$callbackName] = array();
}
$listening = new Listening($listener, $method);
array_push($this->callbackListenings[$callbackName], $listening);
return true;
}
/**
* Register a new Script Callback Listener
*
* @param string $callbackName
* @param CallbackListener $listener
* @param string $method
* @return bool
*/
public function registerScriptCallbackListener($callbackName, CallbackListener $listener, $method) {
if (is_array($callbackName)) {
$success = false;
foreach ($callbackName as $callback) {
if ($this->registerScriptCallbackListener($callback, $listener, $method)) {
$success = true;
}
}
return $success;
}
if (!Listening::checkValidCallback($listener, $method)) {
$listenerClass = get_class($listener);
trigger_error("Given Listener '{$listenerClass}' can't handle Script Callback '{$callbackName}': No callable Method '{$method}'!");
return false;
}
if (!array_key_exists($callbackName, $this->scriptCallbackListenings)) {
$this->scriptCallbackListenings[$callbackName] = array();
}
$listening = new Listening($listener, $method);
array_push($this->scriptCallbackListenings[$callbackName], $listening);
return true;
}
/**
* Unregister a Callback Listener
*
* @param CallbackListener $listener
* @return bool
*/
public function unregisterCallbackListener(CallbackListener $listener) {
return $this->removeCallbackListener($this->callbackListenings, $listener);
}
//TODO better name (used only in customvotesPlugin)
/**
* Remove the Callback Listener from the given Listeners Array
*
* @param Listening[] $listeningsArray
* @param CallbackListener $listener
* @return bool
*/
private function removeCallbackListener(array &$listeningsArray, CallbackListener $listener) {
$removed = false;
foreach ($listeningsArray as &$listenings) {
foreach ($listenings as $key => &$listening) {
if ($listening->listener === $listener) {
unset($listenings[$key]);
$removed = true;
}
}
}
return $removed;
}
/**
* Unregister a single Callback Listening from an Callback Listener
*
* @param String $callbackName
* @param CallbackListener $listener
* @return bool
*/
public function unregisterCallbackListening($callbackName, CallbackListener $listener) {
$removed = false;
foreach ($this->callbackListenings as &$listenings) {
foreach ($listenings as $key => &$listening) {
if ($key === $callbackName && $listening->listener === $listener) {
unset($listenings[$key]);
$removed = true;
}
}
}
return $removed;
}
/**
* Unregister a Script Callback Listener
*
* @param CallbackListener $listener
* @return bool
*/
public function unregisterScriptCallbackListener(CallbackListener $listener) {
return $this->removeCallbackListener($this->scriptCallbackListenings, $listener);
}
/**
* Trigger internal Callbacks and manage Server Callbacks
*/
public function manageCallbacks() {
// Manage Timings
$this->maniaControl->getTimerManager()->manageTimings();
// Server Callbacks
if (!$this->maniaControl->getClient()) {
return;
}
// Handle callbacks
$callbacks = $this->maniaControl->getClient()->executeCallbacks();
foreach ($callbacks as $callback) {
$this->handleCallback($callback);
}
}
/**
* Handle the given Callback
*
* @param array $callback
*/
private function handleCallback(array $callback) {
$callbackName = $callback[0];
switch ($callbackName) {
case self::CB_MP_BEGINMATCH:
$this->triggerCallback($callbackName, $callback);
break;
case self::CB_MP_BEGINMAP:
$this->maniaControl->getMapManager()->handleBeginMap($callback);
$this->triggerCallback($callbackName, $callback);
break;
case self::CB_MP_ENDMATCH:
$this->triggerCallback($callbackName, $callback);
break;
case self::CB_MP_ENDMAP:
$this->maniaControl->getMapManager()->handleEndMap($callback);
$this->triggerCallback($callbackName, $callback);
break;
case self::CB_MP_MODESCRIPTCALLBACK:
$this->handleScriptCallback($callback);
$this->triggerCallback($callbackName, $callback);
break;
case self::CB_MP_MODESCRIPTCALLBACKARRAY:
$this->handleScriptCallback($callback);
$this->triggerCallback($callbackName, $callback);
break;
default:
$this->triggerCallback($callbackName, $callback);
break;
}
}
/**
* Trigger a specific Callback
*
* @param mixed $callback
*/
public function triggerCallback($callback) {
if ($callback instanceof BaseCallback) {
$callbackName = $callback->name;
} else {
$callbackName = $callback;
}
if (!array_key_exists($callbackName, $this->callbackListenings)) {
return;
}
$params = func_get_args();
if (!($callback instanceof BaseCallback)) {
$params = array_slice($params, 1, null, true);
}
foreach ($this->callbackListenings[$callbackName] as $listening) {
/** @var Listening $listening */
$listening->triggerCallbackWithParams($params);
}
}
/**
* Handle the given Script Callback
*
* @param array $callback
*/
private function handleScriptCallback(array $callback) {
$scriptCallbackData = $callback[1];
$scriptCallbackName = $scriptCallbackData[0];
$this->triggerScriptCallback($scriptCallbackName, $scriptCallbackData);
$this->triggerCallback(Callbacks::SCRIPTCALLBACK, $scriptCallbackName, $scriptCallbackData[1]);
}
/**
* Trigger a specific Script Callback
*
* @param string $callbackName
*/
public function triggerScriptCallback($callbackName) {
if (!array_key_exists($callbackName, $this->scriptCallbackListenings)) {
return;
}
$params = func_get_args();
$params = array_slice($params, 1, null, true);
foreach ($this->scriptCallbackListenings[$callbackName] as $listening) {
/** @var Listening $listening */
$listening->triggerCallbackWithParams($params);
}
}
}

View File

@ -0,0 +1,100 @@
<?php
// TODO: method class for all the libxmlrpc get Methods, to fetch the callback asnyc
namespace ManiaControl\Callbacks;
/**
* Callbacks Interface
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
interface Callbacks {
/*
* ManiaControl Callbacks
*/
const ONINIT = 'Callbacks.OnInit';
const AFTERINIT = 'Callbacks.AfterInit';
const ONSHUTDOWN = 'Callbacks.OnShutdown';
/** Script Callback: CallbackName, CallbackData */
const SCRIPTCALLBACK = 'Callbacks.ScriptCallback';
/*
* Common Callbacks
*/
/** BeginMatch Callback: MatchNumber */
const BEGINMATCH = 'Callbacks.BeginMatch';
/** LoadingMap Callback: MapNumber */
const LOADINGMAP = 'Callbacks.LoadingMap';
/** BeginMap Callback: Map */
const BEGINMAP = 'Callbacks.BeginMap';
/** BeginSubMatch Callback: SubmatchNumber */
const BEGINSUBMATCH = 'Callbacks.BeginSubmatch';
/** BeginRound Callback: RoundNumber */
const BEGINROUND = 'Callbacks.BeginRound';
/** BeginTurn Callback: TurnNumber */
const BEGINTURN = 'Callbacks.BeginTurn';
/** BeginPlaying Callback */
const BEGINPLAYING = 'Callbacks.BeginPlaying';
/** EndPlaying Callback */
const ENDPLAYING = 'Callbacks.EndPlaying';
/** EndTurn Callback: TurnNumber */
const ENDTURN = 'Callbacks.EndTurn';
/** EndRound Callback: RoundNumber */
const ENDROUND = 'Callbacks.EndRound';
/** EndSubmatch Callback: SubmatchNumber */
const ENDSUBMATCH = 'Callbacks.EndSubmatch';
/** EndMap Callback: Map */
const ENDMAP = 'Callbacks.EndMap';
/** BeginPodium Callback */
const BEGINPODIUM = 'Callbacks.BeginPodium';
/** EndPodium Callback */
const ENDPODIUM = 'Callbacks.EndPodium';
/** UnloadingMap Callback */
const UNLOADINGMAP = 'Callbacks.UnloadingMap';
/** EndMatch Callback: MatchNumber */
const ENDMATCH = 'Callbacks.EndMatch';
/** BeginWarmup Callback */
const BEGINWARMUP = 'Callbacks.BeginWarmUp';
/** EndWarmup Callback */
const ENDWARMUP = 'Callbacks.EndWarmUp';
/** PlayerRanking Callback, returned after LibXmlRpc_PlayerRanking
* try to avoid to use this, just use the Get function of the RankingsManager instead
* param1 Player $player
* param2 int $rank
* param3 int $currentPoints
* param4 int AFKStatus */
const PLAYERRANKING = 'Callbacks.PlayerRanking';
/*
* ShootMania Callbacks
*/
/** RankingsUpdated Callback: SortedRankings */
const RANKINGSUPDATED = 'Callbacks.RankingsUpdated';
/** Scores Callback (returned after LibXmlRpc_PlayerRanking): Scores */
const SCORES = 'Callbacks.Scores';
/** Returns the AFKStatus of an Player, returned after param1 Scores */ //returned after TODO
const AFKSTATUS = 'Callbacks.AfkStatus';
/** Returns if the GameMode has Warmup activated, returned after param1 Scores */ //returned after TODO
const WARMUPSTATUS = 'Callbacks.WarmupStatus';
/*
* TrackMania Callbacks
*/
/** OnStartLine Callback */
const ONSTARTLINE = 'Callbacks.OnStartLine';
/** OnWayPoint Callback */
const ONWAYPOINT = 'Callbacks.OnWayPoint';
/** OnGiveUp Callback */
const ONGIVEUP = 'Callbacks.OnGiveUp';
/** OnRespawn Callback */
const ONRESPAWN = 'Callbacks.OnRespawn';
/** OnStunt Callback */
const ONSTUNT = 'Callbacks.OnStunt';
}

View File

@ -0,0 +1,128 @@
<?php
namespace ManiaControl\Callbacks;
use ManiaControl\ManiaControl;
/**
* Class converting LibXmlRpc Callbacks
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class LibXmlRpcCallbacks implements CallbackListener {
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/**
* Create a new LibXmlRpc Callbacks Instance
*
* @param ManiaControl $maniaControl
* @param CallbackManager $callbackManager
*/
public function __construct(ManiaControl $maniaControl, CallbackManager $callbackManager) {
$this->maniaControl = $maniaControl;
$callbackManager->registerCallbackListener(Callbacks::SCRIPTCALLBACK, $this, 'handleScriptCallback');
}
/**
* Handle the Script Callback
*
* @param string $name
* @param mixed $data
*/
public function handleScriptCallback($name, $data) {
switch ($name) {
case 'LibXmlRpc_BeginMatch':
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::BEGINMATCH, $data[0]);
break;
case 'LibXmlRpc_LoadingMap':
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::LOADINGMAP, $data[0]);
break;
case 'BeginMap':
case 'LibXmlRpc_BeginMap':
if (!isset($data[2])) {
$data[2] = 'False';
}
$this->maniaControl->getMapManager()->handleScriptBeginMap($data[1], $data[2]);
break;
case 'LibXmlRpc_BeginSubmatch':
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::BEGINSUBMATCH, $data[0]);
break;
case 'LibXmlRpc_BeginTurn':
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::BEGINTURN, $data[0]);
break;
case 'LibXmlRpc_BeginPlaying':
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::BEGINPLAYING);
break;
case 'LibXmlRpc_EndPlaying':
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::ENDPLAYING);
break;
case 'LibXmlRpc_EndTurn':
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::ENDTURN, $data[0]);
break;
case 'LibXmlRpc_EndRound':
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::ENDROUND, $data[0]);
break;
case 'LibXmlRpc_EndSubmatch':
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::ENDSUBMATCH, $data[0]);
break;
case 'EndMap':
case 'LibXmlRpc_EndMap':
$this->maniaControl->getMapManager()->handleScriptEndMap();
break;
case 'LibXmlRpc_BeginPodium':
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::BEGINPODIUM);
break;
case 'LibXmlRpc_EndPodium':
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::ENDPODIUM);
break;
case 'LibXmlRpc_UnloadingMap':
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::UNLOADINGMAP, $data[0]);
break;
case 'LibXmlRpc_EndMatch':
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::ENDMATCH, $data[0]);
break;
case 'LibXmlRpc_BeginWarmUp':
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::BEGINWARMUP);
break;
case 'LibXmlRpc_EndWarmUp':
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::ENDWARMUP);
break;
case 'LibXmlRpc_PlayerRanking':
//TODO really useful? what does it have what RankingsManager not have?
$this->triggerPlayerRanking($data[0]);
break;
case 'LibXmlRpc_OnStartLine':
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::ONSTARTLINE, $data[0]);
break;
case 'LibXmlRpc_OnWayPoint':
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::ONWAYPOINT, $data);
break;
case 'LibXmlRpc_OnGiveUp':
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::ONGIVEUP, $data[0]);
break;
case 'LibXmlRpc_OnRespawn':
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::ONRESPAWN, $data[0]);
break;
case 'LibXmlRpc_OnStunt':
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::ONSTUNT, $data);
break;
}
}
/**
* Trigger the Ranking of a Player
*
* @param array $data
*/
private function triggerPlayerRanking(array $data) {
$player = $this->maniaControl->getPlayerManager()->getPlayer($data[1]);
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::PLAYERRANKING, $player, $data[0], $data[6], $data[5]);
}
}

View File

@ -0,0 +1,76 @@
<?php
namespace ManiaControl\Callbacks;
/**
* Model Class for a Basic Listening
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class Listening {
/*
* Public Properties
*/
public $listener = null;
public $method = null;
/**
* Construct a new Timer Listening
*
* @param object $listener
* @param mixed $method
*/
public function __construct($listener, $method) {
$this->listener = $listener;
$this->method = $method;
}
/**
* Check if the given Listener and Method build a valid Callback
*
* @param object $listener
* @param mixed $method
* @return bool
*/
public static function checkValidCallback($listener, $method) {
if (is_callable($method)) {
return true;
}
$listenerCallback = array($listener, $method);
if (is_callable($listenerCallback)) {
return true;
}
return false;
}
/**
* Trigger the Listener's Method
*/
public function triggerCallback() {
$params = func_get_args();
$this->triggerCallbackWithParams($params);
}
/**
* Trigger the Listener's Method with the given Array of Params
*
* @param array $params
*/
public function triggerCallbackWithParams(array $params) {
call_user_func_array($this->getUserFunction(), $params);
}
/**
* Get the Callable User Function
*
* @return callable
*/
public function getUserFunction() {
if (is_callable($this->method)) {
return $this->method;
}
return array($this->listener, $this->method);
}
}

View File

@ -0,0 +1,37 @@
<?php
namespace ManiaControl\Callbacks\Models;
use ManiaControl\Players\Player;
/**
* Base Model Class for Callbacks
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
abstract class BaseCallback {
/*
* Public Properties
*/
public $name = null;
public $rawCallback = null;
public $isLegacyCallback = null;
public $pid = null;
public $login = null;
/** @var Player $player */
public $player = null;
/**
* Set the corresponding Player
*
* @param Player $player
*/
public function setPlayer(Player $player) {
$this->pid = $player->pid;
$this->login = $player->login;
$this->player = $player;
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace ManiaControl\Callbacks\Models;
/**
* Base Model Class for Callbacks
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class RecordCallback extends BaseCallback {
/*
* Constants
*/
const CHECKPOINT = 'RecordCallback.Checkpoint';
const FINISH = 'RecordCallback.Finish';
const LAPFINISH = 'RecordCallback.LapFinish';
/*
* Public Properties
*/
public $isEndRace = null;
public $isEndLap = null;
public $time = null;
public $lapTime = null;
public $checkpoint = null;
public $lapCheckpoint = null;
public $lap = null;
public $blockId = null;
}

View File

@ -0,0 +1,127 @@
<?php
namespace ManiaControl\Callbacks;
use ManiaControl\Callbacks\Models\RecordCallback;
use ManiaControl\ManiaControl;
/**
* Class handling and parsing ShootMania Callbacks
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class ShootManiaCallbacks implements CallbackListener {
/*
* Constants
*/
const CB_TIMEATTACK_ONSTART = 'TimeAttack_OnStart';
const CB_TIMEATTACK_ONRESTART = 'TimeAttack_OnRestart';
const CB_TIMEATTACK_ONCHECKPOINT = 'TimeAttack_OnCheckpoint';
const CB_TIMEATTACK_ONFINISH = 'TimeAttack_OnFinish';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/**
* Create a new ShootMania Callbacks Instance
*
* @param ManiaControl $maniaControl
* @param CallbackManager $callbackManager
*/
public function __construct(ManiaControl $maniaControl, CallbackManager $callbackManager) {
$this->maniaControl = $maniaControl;
// Register for script callbacks
$callbackManager->registerCallbackListener(Callbacks::SCRIPTCALLBACK, $this, 'handleScriptCallbacks');
}
/**
* Handle Script Callbacks
*
* @param string $name
* @param mixed $data
*/
public function handleScriptCallbacks($name, $data) {
switch ($name) {
case 'LibXmlRpc_Rankings':
$this->maniaControl->getServer()->getRankingManager()->updateRankings($data[0]);
break;
case 'LibXmlRpc_Scores':
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::SCORES, $data);
break;
case 'LibAFK_IsAFK':
$this->triggerAfkStatus($data[0]);
break;
case 'WarmUp_Status':
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::WARMUPSTATUS, $data[0]);
break;
case self::CB_TIMEATTACK_ONCHECKPOINT:
$this->handleTimeAttackOnCheckpoint($name, $data);
break;
case self::CB_TIMEATTACK_ONFINISH:
$this->handleTimeAttackOnFinish($name, $data);
break;
}
}
/**
* Triggers the AFK Status of an Player
*
* @param string $login
*/
private function triggerAfkStatus($login) {
$player = $this->maniaControl->getPlayerManager()->getPlayer($login);
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::AFKSTATUS, $player);
}
/**
* Handle TimeAttack OnCheckpoint Callback
*
* @param string $name
* @param array $data
*/
public function handleTimeAttackOnCheckpoint($name, array $data) {
$login = $data[0];
$player = $this->maniaControl->getPlayerManager()->getPlayer($login);
if (!$player) {
return;
}
// Trigger checkpoint callback
$checkpointCallback = new RecordCallback();
$checkpointCallback->rawCallback = array($name, $data);
$checkpointCallback->name = $checkpointCallback::CHECKPOINT;
$checkpointCallback->setPlayer($player);
$checkpointCallback->time = (int)$data[1];
$this->maniaControl->getCallbackManager()->triggerCallback($checkpointCallback);
}
/**
* Handle TimeAttack OnFinish Callback
*
* @param string $name
* @param array $data
*/
public function handleTimeAttackOnFinish($name, array $data) {
$login = $data[0];
$player = $this->maniaControl->getPlayerManager()->getPlayer($login);
if (!$player) {
return;
}
// Trigger finish callback
$finishCallback = new RecordCallback();
$finishCallback->rawCallback = array($name, $data);
$finishCallback->name = $finishCallback::FINISH;
$finishCallback->setPlayer($player);
$finishCallback->time = (int)$data[1];
$this->maniaControl->getCallbackManager()->triggerCallback($finishCallback);
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace ManiaControl\Callbacks;
/**
* Interface for TimerListener
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
interface TimerListener {
}

View File

@ -0,0 +1,70 @@
<?php
namespace ManiaControl\Callbacks;
/**
* Model Class for a Timer Listening
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class TimerListening extends Listening {
/*
* Public Properties
*/
public $deltaTime = null;
public $oneTime = null;
public $lastTrigger = null;
public $instantCall = null;
/**
* Construct a new Timer Listening
*
* @param TimerListener $listener
* @param mixed $method
* @param float $milliSeconds
* @param bool $oneTime
* @param bool $instantCall
*/
public function __construct(TimerListener $listener, $method, $milliSeconds, $oneTime = false, $instantCall = true) {
parent::__construct($listener, $method);
$this->deltaTime = $milliSeconds / 1000.;
$this->oneTime = (bool)$oneTime;
if ($this->oneTime) {
$this->lastTrigger = time(true);
}
$this->instantCall = (bool)$instantCall;
if (!$this->instantCall) {
$this->lastTrigger = microtime(true);
}
}
/**
* Increase last Trigger Time
*/
public function tick() {
if ($this->lastTrigger === null) {
$this->lastTrigger = microtime(true);
} else {
$this->lastTrigger += $this->deltaTime;
}
}
/**
* Check if the desired Time is reached
*
* @param float $time
* @return bool
*/
public function isTimeReached($time = null) {
if ($this->lastTrigger === null) {
return true;
}
if (!$time) {
$time = microtime(true);
}
return ($this->lastTrigger + $this->deltaTime <= $time);
}
}

View File

@ -0,0 +1,133 @@
<?php
namespace ManiaControl\Callbacks;
use ManiaControl\ManiaControl;
/**
* Class for managing Timed Callbacks
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class TimerManager {
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/** @var TimerListening[] $timerListenings */
private $timerListenings = array();
/**
* Construct a new Timer Manager
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
}
/**
* Registers a One Time Listening
*
* @param TimerListener $listener
* @param string $method
* @param float $milliSeconds
*/
public function registerOneTimeListening(TimerListener $listener, $method, $milliSeconds) {
$this->registerTimerListening($listener, $method, $milliSeconds, true);
}
/**
* Register a Timer Listening, note < 10ms it can get inaccurate
*
* @param TimerListener $listener
* @param string $method
* @param float $milliSeconds
* @param bool $oneTime
* @return bool
*/
public function registerTimerListening(TimerListener $listener, $method, $milliSeconds, $oneTime = false) {
if ((!is_string($method) || !method_exists($listener, $method)) && !is_callable($method)) {
trigger_error("Given Listener (" . get_class($listener) . ") can't handle Timer Callback (No Method '{$method}')!");
return false;
}
// Build Timer Listening
$listening = new TimerListening($listener, $method, $milliSeconds, $oneTime);
$this->addTimerListening($listening);
return true;
}
/**
* Add a Listening to the current List of managed Timers
*
* @param TimerListening $timerListening
*/
public function addTimerListening(TimerListening $timerListening) {
array_push($this->timerListenings, $timerListening);
}
/**
* Unregister a Timer Listening
*
* @param TimerListener $listener
* @param string $method
* @return bool
*/
public function unregisterTimerListening(TimerListener $listener, $method) {
$removed = false;
foreach ($this->timerListenings as $key => &$listening) {
if ($listening->listener === $listener && $listening->method === $method) {
unset($this->timerListenings[$key]);
$removed = true;
}
}
return $removed;
}
/**
* Unregister a Timer Listener
*
* @param TimerListener $listener
* @return bool
*/
public function unregisterTimerListenings(TimerListener $listener) {
$removed = false;
foreach ($this->timerListenings as $key => &$listening) {
if ($listening->listener === $listener) {
unset($this->timerListenings[$key]);
$removed = true;
}
}
return $removed;
}
/**
* Manage the Timings on every ms
*/
public function manageTimings() {
$time = microtime(true);
foreach ($this->timerListenings as $key => $listening) {
/** @var TimerListening $listening */
if (!$listening->isTimeReached($time)) {
continue;
}
if ($listening->oneTime) {
// Unregister one time Listening
unset($this->timerListenings[$key]);
}
$listening->tick();
// Call the User Function
$listening->triggerCallback($time);
}
}
}

View File

@ -0,0 +1,139 @@
<?php
namespace ManiaControl\Callbacks;
use ManiaControl\Callbacks\Models\RecordCallback;
use ManiaControl\ManiaControl;
use ManiaControl\Utils\Formatter;
/**
* Class handling and parsing TrackMania Callbacks
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class TrackManiaCallbacks implements CallbackListener {
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/**
* Create a new TrackMania Callbacks Instance
*
* @param ManiaControl $maniaControl
* @param CallbackManager $callbackManager
*/
public function __construct(ManiaControl $maniaControl, CallbackManager $callbackManager) {
$this->maniaControl = $maniaControl;
// Register for callbacks
$callbackManager->registerCallbackListener(Callbacks::ONWAYPOINT, $this, 'handleOnWayPointCallback');
$callbackManager->registerCallbackListener(CallbackManager::CB_TM_PLAYERCHECKPOINT, $this, 'handlePlayerCheckpointCallback');
$callbackManager->registerCallbackListener(CallbackManager::CB_TM_PLAYERFINISH, $this, 'handlePlayerFinishCallback');
}
/**
* Handle OnWayPoint Callback
*
* @param array $callback
*/
public function handleOnWayPointCallback(array $callback) {
$login = $callback[0];
$player = $this->maniaControl->getPlayerManager()->getPlayer($login);
if (!$player) {
return;
}
// Build callback
$wayPointCallback = new RecordCallback();
$wayPointCallback->rawCallback = $callback;
$wayPointCallback->setPlayer($player);
$wayPointCallback->blockId = $callback[1];
$wayPointCallback->time = (int)$callback[2];
$wayPointCallback->checkpoint = (int)$callback[3];
$wayPointCallback->isEndRace = Formatter::parseBoolean($callback[4]);
$wayPointCallback->lapTime = (int)$callback[5];
$wayPointCallback->lapCheckpoint = (int)$callback[6];
$wayPointCallback->lap = 0;
$wayPointCallback->isEndLap = Formatter::parseBoolean($callback[7]);
if ($wayPointCallback->checkpoint > 0) {
$currentMap = $this->maniaControl->getMapManager()->getCurrentMap();
$wayPointCallback->lap += $wayPointCallback->checkpoint / $currentMap->nbCheckpoints;
}
if ($wayPointCallback->isEndRace) {
$wayPointCallback->name = $wayPointCallback::FINISH;
} else if ($wayPointCallback->isEndLap) {
$wayPointCallback->name = $wayPointCallback::LAPFINISH;
} else {
$wayPointCallback->name = $wayPointCallback::CHECKPOINT;
}
$this->maniaControl->getCallbackManager()->triggerCallback($wayPointCallback);
}
/**
* Handle Hard-Coded Player Checkpoint Callback
*
* @param array $callback
*/
public function handlePlayerCheckpointCallback(array $callback) {
$data = $callback[1];
$login = $data[1];
$player = $this->maniaControl->getPlayerManager()->getPlayer($login);
if (!$player) {
return;
}
// Build checkpoint callback
$checkpointCallback = new RecordCallback();
$checkpointCallback->isLegacyCallback = true;
$checkpointCallback->rawCallback = $callback;
$checkpointCallback->setPlayer($player);
$checkpointCallback->time = (int)$data[2];
$checkpointCallback->lap = (int)$data[3];
$checkpointCallback->checkpoint = (int)$data[4];
$checkpointCallback->lapCheckpoint = $checkpointCallback->checkpoint;
if ($checkpointCallback->lap > 0) {
$currentMap = $this->maniaControl->getMapManager()->getCurrentMap();
$checkpointCallback->lapCheckpoint -= $checkpointCallback->lap * $currentMap->nbCheckpoints;
}
if ($checkpointCallback->lapCheckpoint === 0) {
$checkpointCallback->name = $checkpointCallback::LAPFINISH;
} else {
$checkpointCallback->name = $checkpointCallback::CHECKPOINT;
}
$this->maniaControl->getCallbackManager()->triggerCallback($checkpointCallback);
}
/**
* Handle Hard-Coded Player Finish Callback
*
* @param array $callback
*/
public function handlePlayerFinishCallback(array $callback) {
$data = $callback[1];
$login = $data[1];
$player = $this->maniaControl->getPlayerManager()->getPlayer($login);
if (!$player) {
return;
}
// Build finish callback
$finishCallback = new RecordCallback();
$finishCallback->name = $finishCallback::FINISH;
$finishCallback->isLegacyCallback = true;
$finishCallback->rawCallback = $callback;
$finishCallback->setPlayer($player);
$finishCallback->time = (int)$data[2];
$this->maniaControl->getCallbackManager()->triggerCallback($finishCallback);
}
}

229
core/Chat.php Normal file
View File

@ -0,0 +1,229 @@
<?php
namespace ManiaControl;
use ManiaControl\Admin\AuthenticationManager;
use ManiaControl\Players\Player;
use Maniaplanet\DedicatedServer\Xmlrpc\UnknownPlayerException;
/**
* Chat Utility Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class Chat {
/*
* Constants
*/
const SETTING_PREFIX = 'Messages Prefix';
const SETTING_FORMAT_INFORMATION = 'Information Format';
const SETTING_FORMAT_SUCCESS = 'Success Format';
const SETTING_FORMAT_ERROR = 'Error Format';
const SETTING_FORMAT_USAGEINFO = 'UsageInfo Format';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/**
* Construct chat utility
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Settings
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_PREFIX, '» ');
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_FORMAT_INFORMATION, '$fff');
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_FORMAT_SUCCESS, '$0f0');
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_FORMAT_ERROR, '$f30');
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_FORMAT_USAGEINFO, '$f80');
}
/**
* Send an information message to the given login
*
* @param string $message
* @param string $login
* @param string|bool $prefix
* @return bool
*/
public function sendInformation($message, $login = null, $prefix = true) {
$format = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_FORMAT_INFORMATION);
return $this->sendChat($format . $message, $login, $prefix);
}
/**
* Send a chat message to the given login
*
* @param string $message
* @param string $login
* @param string|bool $prefix
* @return bool
*/
public function sendChat($message, $login = null, $prefix = true) {
if (!$this->maniaControl->getClient()) {
return false;
}
$prefix = $this->buildPrefix($prefix, $login);
$chatMessage = '$<$z$ff0' . $prefix . $message . '$>';
if ($login) {
if (!is_array($login)) {
$login = Player::parseLogin($login);
}
try {
return $this->maniaControl->getClient()->chatSendServerMessage($chatMessage, $login);
} catch (UnknownPlayerException $e) {
return false;
}
}
return $this->maniaControl->getClient()->chatSendServerMessage($chatMessage);
}
/**
* Build the chat message prefix
*
* @param string|bool $prefixParam
* @param string|array $login
* @return string
*/
private function buildPrefix($prefixParam, $login = null) {
if (is_string($prefixParam)) {
return $prefixParam;
}
if ($prefixParam === true) {
$prefix = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_PREFIX);
if ($login) {
// Private - Doubled default prefix
$prefix .= $prefix;
// TODO: validate whether to use specific private & public prefixes instead of just doubling a default one
}
return $prefix;
}
return '';
}
/**
* Send an Error Message to all Connected Admins
*
* @param string $message
* @param int $minLevel
* @param bool $prefix
*/
public function sendErrorToAdmins($message, $minLevel = AuthenticationManager::AUTH_LEVEL_MODERATOR, $prefix = true) {
$format = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_FORMAT_ERROR);
$this->sendMessageToAdmins($format . $message, $minLevel, $prefix);
}
/**
* Send a Message to all connected Admins
*
* @param string $message
* @param int $minLevel
* @param bool|string $prefix
* @return bool
*/
public function sendMessageToAdmins($message, $minLevel = AuthenticationManager::AUTH_LEVEL_MODERATOR, $prefix = true) {
$admins = $this->maniaControl->getAuthenticationManager()->getConnectedAdmins($minLevel);
return $this->sendChat($message, $admins, $prefix);
}
/**
* Send a success message to the given login
*
* @param string $message
* @param string $login
* @param bool|string $prefix
* @return bool
*/
public function sendSuccess($message, $login = null, $prefix = true) {
$format = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_FORMAT_SUCCESS);
return $this->sendChat($format . $message, $login, $prefix);
}
/**
* Sends a Information Message to all connected Admins
*
* @param string $message
* @param int $minLevel
* @param bool|string $prefix
* @return bool
*/
public function sendInformationToAdmins($message, $minLevel = AuthenticationManager::AUTH_LEVEL_MODERATOR, $prefix = true) {
$format = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_FORMAT_INFORMATION);
return $this->sendMessageToAdmins($format . $message, $minLevel, $prefix);
}
/**
* Sends a Success Message to all connected Admins
*
* @param string $message
* @param int $minLevel
* @param bool|string $prefix
* @return bool
*/
public function sendSuccessToAdmins($message, $minLevel = AuthenticationManager::AUTH_LEVEL_MODERATOR, $prefix = true) {
$format = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_FORMAT_SUCCESS);
return $this->sendMessageToAdmins($format . $message, $minLevel, $prefix);
}
/**
* Send the Exception Information to the Chat
*
* @param \Exception $exception
* @param string $login
* @return bool
*/
public function sendException(\Exception $exception, $login = null) {
$message = "Exception occurred: '{$exception->getMessage()}' ({$exception->getCode()})";
return $this->sendError($message, $login);
}
/**
* Send an Error Message to the Chat
*
* @param string $message
* @param string $login
* @param string|bool $prefix
* @return bool
*/
public function sendError($message, $login = null, $prefix = true) {
$format = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_FORMAT_ERROR);
return $this->sendChat($format . $message, $login, $prefix);
}
/**
* Send a Exception Message to all Connected Admins
*
* @param \Exception $exception
* @param int $minLevel
* @param bool|string $prefix
*/
public function sendExceptionToAdmins(\Exception $exception, $minLevel = AuthenticationManager::AUTH_LEVEL_MODERATOR, $prefix = true) {
$format = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_FORMAT_ERROR);
$message = $format . "Exception: '{$exception->getMessage()}' ({$exception->getCode()})";
$this->sendMessageToAdmins($message, $minLevel, $prefix);
}
/**
* Send an usage info message to the given login
*
* @param string $message
* @param string $login
* @param string|bool $prefix
* @return bool
*/
public function sendUsageInfo($message, $login = null, $prefix = false) {
$format = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_FORMAT_USAGEINFO);
return $this->sendChat($format . $message, $login, $prefix);
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace ManiaControl\Commands;
/**
* Interface for Command Listeners
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
interface CommandListener {
}

View File

@ -0,0 +1,220 @@
<?php
namespace ManiaControl\Commands;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\CallbackManager;
use ManiaControl\Callbacks\Listening;
use ManiaControl\ManiaControl;
/**
* Class for handling Chat Commands
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class CommandManager implements CallbackListener {
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/** @var HelpManager $helpManager */
private $helpManager = array();
/** @var Listening[][] $commandListenings */
private $commandListenings = array();
/** @var Listening[][] $adminCommandListenings */
private $adminCommandListenings = array();
/**
* Construct a new Commands Manager
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Children
$this->helpManager = new HelpManager($this->maniaControl);
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_PLAYERCHAT, $this, 'handleChatCallback');
}
/**
* Return the help manager instance
*
* @return HelpManager
*/
public function getHelpManager() {
return $this->helpManager;
}
/**
* Register a Command Listener
*
* @param string $commandName
* @param CommandListener $listener
* @param string $method
* @param bool $adminCommand
* @param string $description
* @return bool
*/
public function registerCommandListener($commandName, CommandListener $listener, $method, $adminCommand = false, $description = null) {
if (is_array($commandName)) {
$success = false;
foreach ($commandName as $command) {
if ($this->registerCommandListener($command, $listener, $method, $adminCommand, $description)) {
$success = true;
}
}
return $success;
}
if (!Listening::checkValidCallback($listener, $method)) {
$listenerClass = get_class($listener);
trigger_error("Given Listener '{$listenerClass}' can't handle Command '{$commandName}': No callable Method '{$method}'!");
return false;
}
$command = strtolower($commandName);
$listening = new Listening($listener, $method);
if ($adminCommand) {
$this->addListening($this->adminCommandListenings, $listening, $command);
} else {
$this->addListening($this->commandListenings, $listening, $command);
}
// TODO: description(?)
if ($description) {
$this->helpManager->registerCommand($command, $adminCommand, $description, get_class($listener) . '\\' . $method);
}
return true;
}
/**
* Add a Listening to the given Listenings Array
*
* @param array $listeningsArray
* @param Listening $listening
* @param string $command
*/
private function addListening(array &$listeningsArray, Listening $listening, $command) {
if (!array_key_exists($command, $listeningsArray) || !is_array($listeningsArray[$command])) {
// Init listenings array
$listeningsArray[$command] = array();
}
// Register command listening
array_push($listeningsArray[$command], $listening);
}
/**
* Unregister a Command Listener
*
* @param CommandListener $listener
* @return bool
*/
public function unregisterCommandListener(CommandListener $listener) {
$removed = false;
if ($this->removeCommandListener($this->commandListenings, $listener)) {
$removed = true;
}
if ($this->removeCommandListener($this->adminCommandListenings, $listener)) {
$removed = true;
}
return $removed;
}
/**
* Remove the Command Listener from the given Listenings Array
*
* @param array $listeningsArray
* @param CommandListener $listener
* @return bool
*/
private function removeCommandListener(array &$listeningsArray, CommandListener $listener) {
$removed = false;
foreach ($listeningsArray as &$listenings) {
foreach ($listenings as $key => &$listening) {
if ($listening->listener === $listener) {
unset($listenings[$key]);
$removed = true;
}
}
}
return $removed;
}
/**
* Handle Chat Callback
*
* @param array $callback
*/
public function handleChatCallback(array $callback) {
// Check for command
if (!$this->isCommandMessage($callback)) {
return;
}
// Check for valid player
$login = $callback[1][1];
$player = $this->maniaControl->getPlayerManager()->getPlayer($login);
if (!$player) {
return;
}
// Parse command
$message = $callback[1][2];
$commandArray = explode(' ', $message);
$command = ltrim(strtolower($commandArray[0]), '/');
if (!$command) {
return;
}
if (substr($message, 0, 2) === '//' || $command === 'admin') {
// Admin command
$commandListenings = $this->adminCommandListenings;
if ($command === 'admin') {
// Strip 'admin' keyword
if (isset($commandArray[1])) {
$command = $commandArray[1];
unset($commandArray[1]);
}
}
unset($commandArray[0]);
// Compose uniformed message
$message = '//' . $command . ' ' . implode(' ', $commandArray);
$callback[1][2] = $message;
} else {
// User command
$commandListenings = $this->commandListenings;
}
if (!array_key_exists($command, $commandListenings) || !is_array($commandListenings[$command])) {
// No command listener registered
return;
}
// Inform command listeners
foreach ($commandListenings[$command] as $listening) {
/** @var Listening $listening */
$listening->triggerCallback($callback, $player);
}
}
/**
* Check if the given Chat Callback is a Command Message
*
* @param array $chatCallback
* @return bool
*/
private function isCommandMessage(array $chatCallback) {
return (bool)$chatCallback[1][3];
}
}

View File

@ -0,0 +1,255 @@
<?php
namespace ManiaControl\Commands;
use FML\Controls\Frame;
use FML\Controls\Quads\Quad_BgsPlayerCard;
use FML\ManiaLink;
use FML\Script\Features\Paging;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\Callbacks;
use ManiaControl\ManiaControl;
use ManiaControl\Manialinks\ManialinkManager;
use ManiaControl\Players\Player;
/**
* ManiaControl Help Manager Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
// TODO: refactor code - i see duplicated code all over the place..
class HelpManager implements CommandListener, CallbackListener {
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
private $playerCommands = array();
private $adminCommands = array();
/**
* Construct a new Commands Manager
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::ONINIT, $this, 'handleOnInit');
}
/**
* Handle ManiaControl OnInit Callback
*/
public function handleOnInit() {
$this->maniaControl->getCommandManager()->registerCommandListener('help', $this, 'command_playerHelp', false, 'Shows all commands in chat.');
$this->maniaControl->getCommandManager()->registerCommandListener('helpall', $this, 'command_playerHelpAll', false, 'Shows all commands in ManiaLink with description.');
$this->maniaControl->getCommandManager()->registerCommandListener('help', $this, 'command_adminHelp', true, 'Shows all admin commands in chat.');
$this->maniaControl->getCommandManager()->registerCommandListener('helpall', $this, 'command_adminHelpAll', true, 'Shows all admin commands in ManiaLink with description.');
}
/**
* Show a list of Admin Commands
*
* @param array $chatCallback
* @param Player $player
*/
public function command_adminHelp(array $chatCallback, Player $player) {
$showCommands = array();
$registeredMethods = array();
foreach (array_reverse($this->adminCommands) as $command) {
if (array_key_exists($command['Method'], $registeredMethods) && $showCommands[$registeredMethods[$command['Method']]]['Description'] === $command['Description']) {
$name = $registeredMethods[$command['Method']];
$showCommands[$name]['Name'] .= '|' . $command['Name'];
} else {
$showCommands[$command['Name']] = $command;
$registeredMethods[$command['Method']] = $command['Name'];
}
}
usort($showCommands, function ($commandA, $commandB) {
return strcmp($commandA['Name'], $commandB['Name']);
});
$message = 'Supported Admin Commands: ';
foreach ($showCommands as $command) {
$message .= $command['Name'] . ',';
}
$message = substr($message, 0, -1);
$this->maniaControl->getChat()->sendChat($message, $player);
}
/**
* Show a list of Player Commands
*
* @param array $chatCallback
* @param Player $player
*/
public function command_playerHelp(array $chatCallback, Player $player) {
$showCommands = array();
$registeredMethods = array();
foreach (array_reverse($this->playerCommands) as $command) {
if (array_key_exists($command['Method'], $registeredMethods) && $showCommands[$registeredMethods[$command['Method']]]['Description'] === $command['Description']) {
$name = $registeredMethods[$command['Method']];
$showCommands[$name]['Name'] .= '|' . $command['Name'];
} else {
$showCommands[$command['Name']] = $command;
$registeredMethods[$command['Method']] = $command['Name'];
}
}
usort($showCommands, function ($commandA, $commandB) {
return strcmp($commandA['Name'], $commandB['Name']);
});
$message = 'Supported Player Commands: ';
foreach ($showCommands as $command) {
$message .= $command['Name'] . ',';
}
$message = substr($message, 0, -1);
$this->maniaControl->getChat()->sendChat($message, $player);
}
/**
* Show a ManiaLink list of Player Commands
*
* @param array $chatCallback
* @param Player $player
*/
public function command_playerHelpAll(array $chatCallback, Player $player) {
$this->prepareHelpAll($this->playerCommands, $player);
}
/**
* Prepare the commands for the HelpAll ManiaLink.
*
* @param array $commands
* @param mixed $player
*/
private function prepareHelpAll(array $commands, $player) {
$showCommands = array();
$registeredMethods = array();
foreach (array_reverse($commands) as $command) {
if (array_key_exists($command['Method'], $registeredMethods)) {
if ($showCommands[$registeredMethods[$command['Method']]]['Description'] === $command['Description']) {
$name = $registeredMethods[$command['Method']];
$showCommands[$name]['Name'] .= '|' . $command['Name'];
} else {
$showCommands[$command['Name']] = $command;
$registeredMethods[$command['Method']] = $command['Name'];
}
} else {
$showCommands[$command['Name']] = $command;
$registeredMethods[$command['Method']] = $command['Name'];
}
}
usort($showCommands, function ($commandA, $commandB) {
return strcmp($commandA['Name'], $commandB['Name']);
});
$this->showHelpAllList($showCommands, $player);
}
/**
* Show the HelpAll list to the player.
*
* @param array $commands
* @param mixed $player
*/
private function showHelpAllList(array $commands, $player) {
$width = $this->maniaControl->getManialinkManager()->getStyleManager()->getListWidgetsWidth();
$height = $this->maniaControl->getManialinkManager()->getStyleManager()->getListWidgetsHeight();
// create manialink
$maniaLink = new ManiaLink(ManialinkManager::MAIN_MLID);
$script = $maniaLink->getScript();
$paging = new Paging();
$script->addFeature($paging);
// Main frame
$frame = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultListFrame($script, $paging);
$maniaLink->add($frame);
// Start offsets
$posX = -$width / 2;
$posY = $height / 2;
//Predefine description Label
$descriptionLabel = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultDescriptionLabel();
$frame->add($descriptionLabel);
// Headline
$headFrame = new Frame();
$frame->add($headFrame);
$headFrame->setY($posY - 5);
$array = array('Command' => $posX + 5, 'Description' => $posX + 50);
$this->maniaControl->getManialinkManager()->labelLine($headFrame, $array);
$index = 1;
$posY -= 10;
$pageFrame = null;
foreach ($commands as $command) {
if ($index % 15 === 1) {
$pageFrame = new Frame();
$frame->add($pageFrame);
$posY = $height / 2 - 10;
$paging->addPage($pageFrame);
}
$playerFrame = new Frame();
$pageFrame->add($playerFrame);
$playerFrame->setY($posY);
if ($index % 2 !== 0) {
$lineQuad = new Quad_BgsPlayerCard();
$playerFrame->add($lineQuad);
$lineQuad->setSize($width, 4);
$lineQuad->setSubStyle($lineQuad::SUBSTYLE_BgPlayerCardBig);
$lineQuad->setZ(0.001);
}
$array = array($command['Name'] => $posX + 5, $command['Description'] => $posX + 50);
$labels = $this->maniaControl->getManialinkManager()->labelLine($playerFrame, $array);
$label = $labels[0];
$label->setWidth(40);
$posY -= 4;
$index++;
}
// Render and display xml
$this->maniaControl->getManialinkManager()->displayWidget($maniaLink, $player, 'HelpAllList');
}
/**
* Show a ManiaLink list of Admin Commands
*
* @param array $chatCallback
* @param Player $player
*/
public function command_adminHelpAll(array $chatCallback, Player $player) {
$this->prepareHelpAll($this->adminCommands, $player);
}
/**
* Register a new Command
*
* @param string $name
* @param bool $adminCommand
* @param string $description
* @param string $method
*/
public function registerCommand($name, $adminCommand = false, $description = '', $method) {
if ($adminCommand) {
array_push($this->adminCommands, array("Name" => $name, "Description" => $description, "Method" => $method));
} else {
array_push($this->playerCommands, array("Name" => $name, "Description" => $description, "Method" => $method));
}
}
}

View File

@ -0,0 +1,350 @@
<?php
namespace ManiaControl\Configurator;
use FML\Controls\Frame;
use FML\Controls\Labels\Label_Text;
use FML\Controls\Quad;
use FML\Controls\Quads\Quad_BgRaceScore2;
use FML\Controls\Quads\Quad_Icons64x64_1;
use FML\Controls\Quads\Quad_UIConstruction_Buttons;
use FML\ManiaLink;
use ManiaControl\Admin\AuthenticationManager;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\CallbackManager;
use ManiaControl\Commands\CommandListener;
use ManiaControl\ManiaControl;
use ManiaControl\Manialinks\ManialinkManager;
use ManiaControl\Manialinks\ManialinkPageAnswerListener;
use ManiaControl\Players\Player;
use ManiaControl\Server\ServerOptionsMenu;
use ManiaControl\Server\VoteRatiosMenu;
/**
* Class managing ingame ManiaControl Configuration
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class Configurator implements CallbackListener, CommandListener, ManialinkPageAnswerListener {
/*
* Constants
*/
const ACTION_TOGGLEMENU = 'Configurator.ToggleMenuAction';
const ACTION_SAVECONFIG = 'Configurator.SaveConfigAction';
const ACTION_SELECTMENU = 'Configurator.SelectMenu.';
const SETTING_MENU_POSX = 'Menu Widget Position: X';
const SETTING_MENU_POSY = 'Menu Widget Position: Y';
const SETTING_MENU_WIDTH = 'Menu Widget Width';
const SETTING_MENU_HEIGHT = 'Menu Widget Height';
const SETTING_MENU_STYLE = 'Menu Widget BackgroundQuad Style';
const SETTING_MENU_SUBSTYLE = 'Menu Widget BackgroundQuad Substyle';
const SETTING_PERMISSION_OPEN_CONFIGURATOR = 'Open Configurator';
const CACHE_MENU_SHOWN = 'MenuShown';
const MENU_NAME = 'Configurator';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/** @var ServerOptionsMenu $serverOptionsMenu */
private $serverOptionsMenu = null;
/** @var ScriptSettings $scriptSettings */
private $scriptSettings = null;
/** @var VoteRatiosMenu $voteRatiosMenu */
private $voteRatiosMenu = null;
/** @var ManiaControlSettings $maniaControlSettings */
private $maniaControlSettings = null;
/** @var ConfiguratorMenu[] $menus */
private $menus = array();
/**
* Create a new configurator instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
$this->addActionsMenuItem();
// Settings
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_MENU_POSX, 0.);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_MENU_POSY, 3.);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_MENU_WIDTH, 170.);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_MENU_HEIGHT, 81.);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_MENU_STYLE, Quad_BgRaceScore2::STYLE);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_MENU_SUBSTYLE, Quad_BgRaceScore2::SUBSTYLE_HandleSelectable);
// Permissions
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_OPEN_CONFIGURATOR, AuthenticationManager::AUTH_LEVEL_ADMIN);
// Page answers
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ACTION_TOGGLEMENU, $this, 'handleToggleMenuAction');
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ACTION_SAVECONFIG, $this, 'handleSaveConfigAction');
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_PLAYERMANIALINKPAGEANSWER, $this, 'handleManialinkPageAnswer');
$this->maniaControl->getCallbackManager()->registerCallbackListener(ManialinkManager::CB_MAIN_WINDOW_OPENED, $this, 'handleWidgetOpened');
$this->maniaControl->getCallbackManager()->registerCallbackListener(ManialinkManager::CB_MAIN_WINDOW_CLOSED, $this, 'closeWidget');
// Create server options menu
$this->serverOptionsMenu = new ServerOptionsMenu($maniaControl);
$this->addMenu($this->serverOptionsMenu);
// Create script settings
$this->scriptSettings = new ScriptSettings($maniaControl);
$this->addMenu($this->scriptSettings);
// Create vote ratios menu
$this->voteRatiosMenu = new VoteRatiosMenu($maniaControl);
$this->addMenu($this->voteRatiosMenu);
// Create Mania Control Settings
$this->maniaControlSettings = new ManiaControlSettings($maniaControl);
$this->addMenu($this->maniaControlSettings);
// Chat commands
$this->maniaControl->getCommandManager()->registerCommandListener('config', $this, 'handleConfigCommand', true, 'Loads Config panel.');
}
/**
* Add Menu Item to the Actions Menu
*/
private function addActionsMenuItem() {
$itemQuad = new Quad_UIConstruction_Buttons();
$itemQuad->setSubStyle($itemQuad::SUBSTYLE_Tools)->setAction(self::ACTION_TOGGLEMENU);
$this->maniaControl->getActionsMenu()->addAdminMenuItem($itemQuad, 100, 'Settings');
}
/**
* Add a configurator menu
*
* @param ConfiguratorMenu $menu
*/
public function addMenu(ConfiguratorMenu $menu) {
array_push($this->menus, $menu);
}
/**
* Handle Config Admin Command
*
* @param array $callback
* @param Player $player
*/
public function handleConfigCommand(array $callback, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_OPEN_CONFIGURATOR)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$this->showMenu($player);
}
/**
* Show the Menu to the Player
*
* @param Player $player
* @param mixed $menuId
*/
public function showMenu(Player $player, $menuId = 0) {
if ($menuId instanceof ConfiguratorMenu) {
$menuId = $this->getMenuId($menuId->getTitle());
}
$manialink = $this->buildManialink($menuId, $player);
$this->maniaControl->getManialinkManager()->displayWidget($manialink, $player, self::MENU_NAME);
$player->setCache($this, self::CACHE_MENU_SHOWN, true);
}
/**
* Gets the Menu Id
*
* @param string $title
* @return int
*/
public function getMenuId($title) {
$index = 0;
foreach ($this->menus as $menu) {
if ($menu === $title || $menu->getTitle() === $title) {
return $index;
}
$index++;
}
return 0;
}
/**
* Build Menu ManiaLink if necessary
*
* @param int $menuIdShown
* @param Player $player
* @return \FML\ManiaLink
*/
private function buildManialink($menuIdShown = 0, Player $player = null) {
$menuPosX = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_MENU_POSX);
$menuPosY = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_MENU_POSY);
$menuWidth = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_MENU_WIDTH);
$menuHeight = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_MENU_HEIGHT);
$quadStyle = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_MENU_STYLE);
$quadSubstyle = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_MENU_SUBSTYLE);
$menuListWidth = $menuWidth * 0.3;
$menuItemHeight = 10.;
$subMenuWidth = $menuWidth - $menuListWidth;
$subMenuHeight = $menuHeight;
$manialink = new ManiaLink(ManialinkManager::MAIN_MLID);
$frame = new Frame();
$manialink->add($frame);
$frame->setPosition($menuPosX, $menuPosY, 10);
$backgroundQuad = new Quad();
$frame->add($backgroundQuad);
$backgroundQuad->setZ(-10)->setSize($menuWidth, $menuHeight)->setStyles($quadStyle, $quadSubstyle);
$menuItemsFrame = new Frame();
$frame->add($menuItemsFrame);
$menuItemsFrame->setX($menuWidth * -0.5 + $menuListWidth * 0.5);
$itemsBackgroundQuad = new Quad();
$menuItemsFrame->add($itemsBackgroundQuad);
$backgroundQuad->setZ(-9);
$itemsBackgroundQuad->setSize($menuListWidth, $menuHeight)->setStyles($quadStyle, $quadSubstyle);
$menusFrame = new Frame();
$frame->add($menusFrame);
$menusFrame->setX($menuWidth * -0.5 + $menuListWidth + $subMenuWidth * 0.5);
// Create script and features
$script = $manialink->getScript();
$menuItemY = $menuHeight * 0.42;
$menuId = 0;
foreach ($this->menus as $menu) {
// Add title
$menuItemLabel = new Label_Text();
$menuItemsFrame->add($menuItemLabel);
$menuItemLabel->setY($menuItemY)->setSize($menuListWidth * 0.9, $menuItemHeight * 0.9)->setStyle($menuItemLabel::STYLE_TextCardRaceRank)->setText($menu->getTitle())->setAction(self::ACTION_SELECTMENU . $menuId);
// Show the menu
if ($menuId === $menuIdShown) {
$menuControl = $menu->getMenu($subMenuWidth, $subMenuHeight, $script, $player);
if ($menuControl) {
$menusFrame->add($menuControl);
} else {
$this->maniaControl->getChat()->sendError('Error loading Menu!', $player);
}
}
$menuItemY -= $menuItemHeight * 1.1;
$menuId++;
}
// Add Close Quad (X)
$closeQuad = new Quad_Icons64x64_1();
$frame->add($closeQuad);
$closeQuad->setPosition($menuWidth * 0.483, $menuHeight * 0.467, 3)->setSize(6, 6)->setSubStyle($closeQuad::SUBSTYLE_QuitRace)->setAction(ManialinkManager::ACTION_CLOSEWIDGET);
// Add close button
$closeButton = new Label_Text();
$frame->add($closeButton);
$closeButton->setPosition($menuWidth * -0.5 + $menuListWidth * 0.29, $menuHeight * -0.43)->setSize($menuListWidth * 0.3, $menuListWidth * 0.1)->setStyle($closeButton::STYLE_TextButtonNavBack)->setTextPrefix('$999')->setTranslate(true)->setText('Close')->setAction(self::ACTION_TOGGLEMENU);
// Add save button
$saveButton = new Label_Text();
$frame->add($saveButton);
$saveButton->setPosition($menuWidth * -0.5 + $menuListWidth * 0.71, $menuHeight * -0.43)->setSize($menuListWidth * 0.3, $menuListWidth * 0.1)->setStyle($saveButton::STYLE_TextButtonNavBack)->setTextPrefix('$0f5')->setTranslate(true)->setText('Save')->setAction(self::ACTION_SAVECONFIG);
return $manialink;
}
/**
* Handle toggle menu action
*
* @param array $callback
* @param Player $player
*/
public function handleToggleMenuAction(array $callback, Player $player) {
$this->toggleMenu($player);
}
/**
* Toggle the Menu for the Player
*
* @param Player $player
*/
public function toggleMenu(Player $player) {
if ($player->getCache($this, self::CACHE_MENU_SHOWN)) {
$this->hideMenu($player);
} else {
$this->showMenu($player);
}
}
/**
* Hide the Menu for the Player
*
* @param Player $player
*/
public function hideMenu(Player $player) {
$this->closeWidget($player);
$this->maniaControl->getManialinkManager()->closeWidget($player);
}
/**
* Handle widget being closed
*
* @param Player $player
*/
public function closeWidget(Player $player) {
$player->destroyCache($this, self::CACHE_MENU_SHOWN);
}
/**
* Save the config data received from the manialink
*
* @param array $callback
* @param Player $player
*/
public function handleSaveConfigAction(array $callback, Player $player) {
foreach ($this->menus as $menu) {
$menu->saveConfigData($callback[1], $player);
}
}
/**
* Unset the player if he opened another Main Widget
*
* @param Player $player
* @param string $openedWidget
*/
public function handleWidgetOpened(Player $player, $openedWidget) {
if ($openedWidget !== self::MENU_NAME) {
$player->destroyCache($this, self::CACHE_MENU_SHOWN);
}
}
/**
* Handle ManialinkPageAnswer Callback
*
* @param array $callback
*/
public function handleManialinkPageAnswer(array $callback) {
$actionId = $callback[1][2];
$boolSelectMenu = (strpos($actionId, self::ACTION_SELECTMENU) === 0);
if (!$boolSelectMenu) {
return;
}
$login = $callback[1][1];
$player = $this->maniaControl->getPlayerManager()->getPlayer($login);
if ($player) {
$actionArray = explode('.', $callback[1][2]);
$this->showMenu($player, intval($actionArray[2]));
}
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace ManiaControl\Configurator;
use FML\Script\Script;
use ManiaControl\Players\Player;
/**
* Interface for Configurator Menus
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
interface ConfiguratorMenu {
/**
* Get the menu title
*
* @return string
*/
public static function getTitle();
/**
* Get the configurator menu frame
*
* @param float $width
* @param float $height
* @param Script $script
* @param Player $player
* @return \FML\Controls\Frame
*/
public function getMenu($width, $height, Script $script, Player $player);
/**
* Save the config data
*
* @param array $configData
* @param Player $player
*/
public function saveConfigData(array $configData, Player $player);
}

View File

@ -0,0 +1,352 @@
<?php
namespace ManiaControl\Configurator;
use FML\Components\CheckBox;
use FML\Components\ValuePicker;
use FML\Controls\Entry;
use FML\Controls\Frame;
use FML\Controls\Labels\Label_Button;
use FML\Controls\Labels\Label_Text;
use FML\Controls\Quad;
use FML\Controls\Quads\Quad_Icons64x64_1;
use FML\Script\Features\Paging;
use FML\Script\Script;
use ManiaControl\Admin\AuthenticationManager;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\CallbackManager;
use ManiaControl\ManiaControl;
use ManiaControl\Players\Player;
use ManiaControl\Settings\Setting;
/**
* Class offering a Configurator for ManiaControl Settings
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class ManiaControlSettings implements ConfiguratorMenu, CallbackListener {
/*
* Constants
*/
const TITLE = 'ManiaControl Settings';
const ACTION_PREFIX_SETTING = 'MCSetting.';
const ACTION_PREFIX_SETTINGCLASS = 'MCSettingClass.';
const ACTION_SETTINGCLASS_BACK = 'MCSettingClassBack';
const SETTING_PERMISSION_CHANGE_MC_SETTINGS = 'Change ManiaControl Settings';
const CACHE_CLASS_OPENED = 'ClassOpened';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/**
* Construct a new ManiaControl Settings instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_PLAYERMANIALINKPAGEANSWER, $this, 'handleManialinkPageAnswer');
// Permissions
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_CHANGE_MC_SETTINGS, AuthenticationManager::AUTH_LEVEL_ADMIN);
}
/**
* @see \ManiaControl\Configurators\ConfiguratorMenu::getTitle()
*/
public static function getTitle() {
return self::TITLE;
}
/**
* @see \ManiaControl\Configurators\ConfiguratorMenu::getMenu()
*/
public function getMenu($width, $height, Script $script, Player $player) {
$openedClass = $player->getCache($this, self::CACHE_CLASS_OPENED);
if ($openedClass) {
return $this->getMenuSettingsForClass($openedClass, $width, $height, $script, $player);
}
return $this->getMenuSettingClasses($width, $height, $script, $player);
}
/**
* Get the Menu showing the Settings for the given Class
*
* @param string $settingClass
* @param float $width
* @param float $height
* @param Script $script
* @param Player $player
* @return \FML\Controls\Frame
*/
private function getMenuSettingsForClass($settingClass, $width, $height, Script $script, Player $player) {
$settings = $this->maniaControl->getSettingManager()->getSettingsByClass($settingClass);
$paging = new Paging();
$script->addFeature($paging);
$frame = new Frame();
// Config
$pagerSize = 9.;
$settingHeight = 5.;
$labelTextSize = 2;
$pageMaxCount = 11;
// Pagers
$pagerPrev = new Quad_Icons64x64_1();
$frame->add($pagerPrev);
$pagerPrev->setPosition($width * 0.39, $height * -0.44, 2);
$pagerPrev->setSize($pagerSize, $pagerSize);
$pagerPrev->setSubStyle($pagerPrev::SUBSTYLE_ArrowPrev);
$pagerNext = new Quad_Icons64x64_1();
$frame->add($pagerNext);
$pagerNext->setPosition($width * 0.45, $height * -0.44, 2);
$pagerNext->setSize($pagerSize, $pagerSize);
$pagerNext->setSubStyle($pagerNext::SUBSTYLE_ArrowNext);
$paging->addButton($pagerNext);
$paging->addButton($pagerPrev);
$pageCountLabel = new Label_Text();
$frame->add($pageCountLabel);
$pageCountLabel->setHAlign($pageCountLabel::RIGHT);
$pageCountLabel->setPosition($width * 0.35, $height * -0.44);
$pageCountLabel->setStyle($pageCountLabel::STYLE_TextTitle1);
$pageCountLabel->setTextSize(2);
$paging->setLabel($pageCountLabel);
$backLabel = new Label_Button();
$frame->add($backLabel);
$backLabel->setStyle($backLabel::STYLE_CardMain_Quit);
$backLabel->setPosition(-$width / 2 + 7, -$height / 2 + 7);
$backLabel->setHAlign($backLabel::LEFT);
$backLabel->setTextSize(2);
$backLabel->setText('Back');
$backLabel->setAction(self::ACTION_SETTINGCLASS_BACK);
$headLabel = new Label_Text();
$frame->add($headLabel);
$headLabel->setHAlign($headLabel::LEFT);
$headLabel->setPosition($width * -0.46, $height * 0.41);
$headLabel->setSize($width * 0.6, $settingHeight);
$headLabel->setStyle($headLabel::STYLE_TextCardSmall);
$headLabel->setTextSize(3);
$headLabel->setText($settingClass);
$headLabel->setTextColor('ff0');
$pageFrame = null;
$index = 0;
$posY = 0;
foreach ($settings as $setting) {
if ($index % $pageMaxCount === 0) {
$pageFrame = new Frame();
$frame->add($pageFrame);
$paging->addPage($pageFrame);
$posY = $height * 0.41 - $settingHeight * 1.5;
}
$settingFrame = new Frame();
$pageFrame->add($settingFrame);
$settingFrame->setY($posY);
$nameLabel = new Label_Text();
$settingFrame->add($nameLabel);
$nameLabel->setHAlign($nameLabel::LEFT);
$nameLabel->setX($width * -0.46);
$nameLabel->setSize($width * 0.6, $settingHeight);
$nameLabel->setStyle($nameLabel::STYLE_TextCardSmall);
$nameLabel->setTextSize($labelTextSize);
$nameLabel->setText($setting->setting);
$nameLabel->setTextColor('fff');
$settingName = self::ACTION_PREFIX_SETTING . $setting->index;
if ($setting->type === Setting::TYPE_BOOL) {
// Boolean checkbox
$quad = new Quad();
$quad->setPosition($width * 0.33, 0, -0.01);
$quad->setSize(4, 4);
$checkBox = new CheckBox($settingName, $setting->value, $quad);
$settingFrame->add($checkBox);
} else if ($setting->type === Setting::TYPE_SET) {
// SET value picker
$label = new Label_Text();
$label->setX($width * 0.33);
$label->setSize($width * 0.3, $settingHeight * 0.9);
$label->setStyle($label::STYLE_TextValueSmall);
$label->setTextSize(1);
$valuePicker = new ValuePicker($settingName, $setting->set, $setting->value, $label);
$settingFrame->add($valuePicker);
} else {
// Standard entry
$entry = new Entry();
$settingFrame->add($entry);
$entry->setX($width * 0.33);
$entry->setSize($width * 0.3, $settingHeight * 0.9);
$entry->setStyle(Label_Text::STYLE_TextValueSmall);
$entry->setTextSize(1);
$entry->setName($settingName);
$entry->setDefault($setting->value);
}
$posY -= $settingHeight;
$index++;
}
return $frame;
}
/**
* Get the Menu showing all possible Classes
*
* @param float $width
* @param float $height
* @param Script $script
* @param Player $player
* @return \FML\Controls\Frame
*/
private function getMenuSettingClasses($width, $height, Script $script, Player $player) {
$settingClasses = $this->maniaControl->getSettingManager()->getSettingClasses(true);
$paging = new Paging();
$script->addFeature($paging);
$frame = new Frame();
// Config
$pagerSize = 9.;
$settingHeight = 5.;
$pageMaxCount = 13;
$posY = 0;
// Pagers
$pagerPrev = new Quad_Icons64x64_1();
$frame->add($pagerPrev);
$pagerPrev->setPosition($width * 0.39, $height * -0.44, 2);
$pagerPrev->setSize($pagerSize, $pagerSize);
$pagerPrev->setSubStyle($pagerPrev::SUBSTYLE_ArrowPrev);
$pagerNext = new Quad_Icons64x64_1();
$frame->add($pagerNext);
$pagerNext->setPosition($width * 0.45, $height * -0.44, 2);
$pagerNext->setSize($pagerSize, $pagerSize);
$pagerNext->setSubStyle($pagerNext::SUBSTYLE_ArrowNext);
$paging->addButton($pagerNext);
$paging->addButton($pagerPrev);
$pageCountLabel = new Label_Text();
$frame->add($pageCountLabel);
$pageCountLabel->setHAlign($pageCountLabel::RIGHT);
$pageCountLabel->setPosition($width * 0.35, $height * -0.44, 1);
$pageCountLabel->setStyle($pageCountLabel::STYLE_TextTitle1);
$pageCountLabel->setTextSize(2);
$paging->setLabel($pageCountLabel);
$pageFrame = null;
$index = 0;
foreach ($settingClasses as $settingClass) {
if ($index % $pageMaxCount === 0) {
$pageFrame = new Frame();
$frame->add($pageFrame);
$posY = $height * 0.41;
$paging->addPage($pageFrame);
}
$classLabel = new Label_Text();
$settingClassArray = explode('\\', $settingClass);
$className = '';
for ($i = 1; $i < count($settingClassArray); $i++) {
$className .= $settingClassArray[$i] . ' - ';
}
$className = substr($className, 0, -3);
$pageFrame->add($classLabel);
$classLabel->setHAlign($classLabel::LEFT);
$classLabel->setPosition($width * -0.45, $posY);
$classLabel->setSize($width * 0.9, $settingHeight * 0.9);
$classLabel->setStyle($classLabel::STYLE_TextCardSmall);
$classLabel->setTextSize(2);
$classLabel->setText($className);
$classLabel->setTextColor('fff');
$classLabel->setAction(self::ACTION_PREFIX_SETTINGCLASS . $settingClass);
$posY -= $settingHeight;
$index++;
}
return $frame;
}
/**
* Handle ManialinkPageAnswer Callback
*
* @param array $callback
*/
public function handleManialinkPageAnswer(array $callback) {
$actionId = $callback[1][2];
if ($actionId === self::ACTION_SETTINGCLASS_BACK) {
// Back to classes list
$login = $callback[1][1];
$player = $this->maniaControl->getPlayerManager()->getPlayer($login);
$player->destroyCache($this, self::CACHE_CLASS_OPENED);
$menuId = $this->maniaControl->getConfigurator()->getMenuId($this);
$this->maniaControl->getConfigurator()->showMenu($player, $menuId);
} else if (strpos($actionId, self::ACTION_PREFIX_SETTINGCLASS) === 0) {
// Setting class selected
$settingClass = substr($actionId, strlen(self::ACTION_PREFIX_SETTINGCLASS));
$login = $callback[1][1];
$player = $this->maniaControl->getPlayerManager()->getPlayer($login);
$player->setCache($this, self::CACHE_CLASS_OPENED, $settingClass);
$menuId = $this->maniaControl->getConfigurator()->getMenuId($this);
$this->maniaControl->getConfigurator()->showMenu($player, $menuId);
}
}
/**
* @see \ManiaControl\Configurators\ConfiguratorMenu::saveConfigData()
*/
public function saveConfigData(array $configData, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CHANGE_MC_SETTINGS)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
if (!$configData[3] || strpos($configData[3][0]['Name'], self::ACTION_PREFIX_SETTING) !== 0) {
return;
}
$prefixLength = strlen(self::ACTION_PREFIX_SETTING);
foreach ($configData[3] as $settingData) {
$settingIndex = (int)substr($settingData['Name'], $prefixLength);
$settingObject = $this->maniaControl->getSettingManager()->getSettingObjectByIndex($settingIndex);
if (!$settingObject) {
continue;
}
if (!$settingData || $settingData['Value'] == $settingObject->value) {
continue;
}
$settingObject->value = $settingData['Value'];
$this->maniaControl->getSettingManager()->saveSetting($settingObject);
}
$this->maniaControl->getChat()->sendSuccess('Settings saved!', $player);
// Reopen the Menu
$this->maniaControl->getConfigurator()->showMenu($player, $this);
}
}

View File

@ -0,0 +1,411 @@
<?php
namespace ManiaControl\Configurator;
use FML\Components\CheckBox;
use FML\Controls\Entry;
use FML\Controls\Frame;
use FML\Controls\Label;
use FML\Controls\Labels\Label_Text;
use FML\Controls\Quad;
use FML\Controls\Quads\Quad_Icons64x64_1;
use FML\Script\Features\Paging;
use FML\Script\Script;
use ManiaControl\Admin\AuthenticationManager;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\Callbacks;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
use ManiaControl\Players\Player;
use Maniaplanet\DedicatedServer\Xmlrpc\GameModeException;
/**
* Class offering a Configurator for Script Settings
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class ScriptSettings implements ConfiguratorMenu, CallbackListener {
/*
* Constants
*/
const ACTION_PREFIX_SETTING = 'ScriptSetting.';
const CB_SCRIPTSETTING_CHANGED = 'ScriptSettings.SettingChanged';
const CB_SCRIPTSETTINGS_CHANGED = 'ScriptSettings.SettingsChanged';
const TABLE_SCRIPT_SETTINGS = 'mc_scriptsettings';
const SETTING_LOAD_DEFAULT_SETTINGS_MAP_BEGIN = 'Load Stored Script-Settings on Map-Begin';
const SETTING_PERMISSION_CHANGE_SCRIPT_SETTINGS = 'Change Script-Settings';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/**
* Construct a new script settings instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
$this->initTables();
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::ONINIT, $this, 'onInit');
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::BEGINMAP, $this, 'onBeginMap');
// Settings
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_LOAD_DEFAULT_SETTINGS_MAP_BEGIN, false);
// Permissions
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_CHANGE_SCRIPT_SETTINGS, AuthenticationManager::AUTH_LEVEL_ADMIN);
}
/**
* Create all necessary database tables
*
* @return boolean
*/
private function initTables() {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$query = "CREATE TABLE IF NOT EXISTS `" . self::TABLE_SCRIPT_SETTINGS . "` (
`index` int(11) NOT NULL AUTO_INCREMENT,
`serverIndex` int(11) NOT NULL,
`settingName` varchar(100) NOT NULL,
`settingValue` varchar(500) NOT NULL,
PRIMARY KEY (`index`),
UNIQUE KEY `setting` (`serverIndex`, `settingName`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Script Settings' AUTO_INCREMENT=1;";
$statement = $mysqli->prepare($query);
if ($mysqli->error) {
trigger_error($mysqli->error, E_USER_ERROR);
return false;
}
$statement->execute();
if ($statement->error) {
trigger_error($statement->error, E_USER_ERROR);
return false;
}
$statement->close();
return true;
}
/**
* @see \ManiaControl\Configurators\ConfiguratorMenu::getTitle()
*/
public static function getTitle() {
return 'Script Settings';
}
/**
* Handle OnInit callback
*/
public function onInit() {
$this->loadSettingsFromDatabase();
}
/**
* Load Settings from Database
*
* @return bool
*/
public function loadSettingsFromDatabase() {
try {
$scriptSettings = $this->maniaControl->getClient()->getModeScriptSettings();
} catch (GameModeException $e) {
return false;
}
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$serverIndex = $this->maniaControl->getServer()->index;
$query = "SELECT * FROM `" . self::TABLE_SCRIPT_SETTINGS . "`
WHERE serverIndex = {$serverIndex};";
$result = $mysqli->query($query);
if ($mysqli->error) {
trigger_error($mysqli->error);
return false;
}
$loadedSettings = array();
while ($row = $result->fetch_object()) {
if (!isset($scriptSettings[$row->settingName])) {
continue;
}
$loadedSettings[$row->settingName] = $row->settingValue;
settype($loadedSettings[$row->settingName], gettype($scriptSettings[$row->settingName]));
}
$result->free();
if (empty($loadedSettings)) {
return true;
}
return $this->maniaControl->getClient()->setModeScriptSettings($loadedSettings);
}
/**
* Handle Begin Map Callback
*/
public function onBeginMap() {
if ($this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_LOAD_DEFAULT_SETTINGS_MAP_BEGIN)
) {
$this->loadSettingsFromDatabase();
}
}
/**
* @see \ManiaControl\Configurators\ConfiguratorMenu::getMenu()
*/
public function getMenu($width, $height, Script $script, Player $player) {
$paging = new Paging();
$script->addFeature($paging);
$frame = new Frame();
try {
$scriptInfo = $this->maniaControl->getClient()->getModeScriptInfo();
} catch (GameModeException $e) {
$label = new Label();
$frame->add($label);
$label->setText($e->getMessage());
return $frame;
}
$scriptParams = $scriptInfo->paramDescs;
try {
$scriptSettings = $this->maniaControl->getClient()->getModeScriptSettings();
} catch (GameModeException $e) {
}
// Config
$pagerSize = 9.;
$settingHeight = 5.;
$labelTextSize = 2;
// Pagers
$pagerPrev = new Quad_Icons64x64_1();
$frame->add($pagerPrev);
$pagerPrev->setPosition($width * 0.39, $height * -0.44, 2);
$pagerPrev->setSize($pagerSize, $pagerSize);
$pagerPrev->setSubStyle($pagerPrev::SUBSTYLE_ArrowPrev);
$pagerNext = new Quad_Icons64x64_1();
$frame->add($pagerNext);
$pagerNext->setPosition($width * 0.45, $height * -0.44, 2);
$pagerNext->setSize($pagerSize, $pagerSize);
$pagerNext->setSubStyle($pagerNext::SUBSTYLE_ArrowNext);
$paging->addButton($pagerNext);
$paging->addButton($pagerPrev);
$pageCountLabel = new Label_Text();
$frame->add($pageCountLabel);
$pageCountLabel->setHAlign($pageCountLabel::RIGHT);
$pageCountLabel->setPosition($width * 0.35, $height * -0.44, 1);
$pageCountLabel->setStyle($pageCountLabel::STYLE_TextTitle1);
$pageCountLabel->setTextSize(2);
$paging->setLabel($pageCountLabel);
// Setting pages
$pageFrame = null;
$posY = 0.;
foreach ($scriptParams as $index => $scriptParam) {
/** @var \Maniaplanet\DedicatedServer\Structures\ScriptSettings $scriptParam */
$settingName = $scriptParam->name;
if (!isset($scriptSettings[$settingName])) {
continue;
}
if ($index % 13 === 0) {
$pageFrame = new Frame();
$frame->add($pageFrame);
$posY = $height * 0.41;
$paging->addPage($pageFrame);
}
$settingFrame = new Frame();
$pageFrame->add($settingFrame);
$settingFrame->setY($posY);
$nameLabel = new Label_Text();
$settingFrame->add($nameLabel);
$nameLabel->setHAlign($nameLabel::LEFT);
$nameLabel->setX($width * -0.46);
$nameLabel->setSize($width * 0.4, $settingHeight);
$nameLabel->setStyle($nameLabel::STYLE_TextCardSmall);
$nameLabel->setTextSize($labelTextSize);
$nameLabel->setText($settingName);
$settingValue = $scriptSettings[$settingName];
if (is_bool($settingValue)) {
// Boolean checkbox
$quad = new Quad();
$quad->setX($width / 2 * 0.545);
$quad->setSize(4, 4);
$checkBox = new CheckBox(self::ACTION_PREFIX_SETTING . $settingName, $settingValue, $quad);
$settingFrame->add($checkBox);
} else {
// Value entry
$entry = new Entry();
$settingFrame->add($entry);
$entry->setStyle(Label_Text::STYLE_TextValueSmall);
$entry->setX($width / 2 * 0.55);
$entry->setTextSize(1);
$entry->setSize($width * 0.3, $settingHeight * 0.9);
$entry->setName(self::ACTION_PREFIX_SETTING . $settingName);
$entry->setDefault($settingValue);
}
$descriptionLabel = new Label();
$pageFrame->add($descriptionLabel);
$descriptionLabel->setHAlign($descriptionLabel::LEFT);
$descriptionLabel->setPosition($width * -0.45, $height * -0.44);
$descriptionLabel->setSize($width * 0.7, $settingHeight);
$descriptionLabel->setTextSize($labelTextSize);
$descriptionLabel->setTranslate(true);
$descriptionLabel->setText($scriptParam->desc);
$nameLabel->addTooltipFeature($descriptionLabel);
$posY -= $settingHeight;
}
return $frame;
}
/**
* @see \ManiaControl\Configurators\ConfiguratorMenu::saveConfigData()
*/
public function saveConfigData(array $configData, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CHANGE_SCRIPT_SETTINGS)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
if (!$configData[3] || strpos($configData[3][0]['Name'], self::ACTION_PREFIX_SETTING) !== 0) {
return;
}
try {
$scriptSettings = $this->maniaControl->getClient()->getModeScriptSettings();
} catch (GameModeException $e) {
return;
}
$prefixLength = strlen(self::ACTION_PREFIX_SETTING);
$newSettings = array();
foreach ($configData[3] as $setting) {
$settingName = substr($setting['Name'], $prefixLength);
if (!isset($scriptSettings[$settingName])) {
var_dump('no setting ' . $settingName);
continue;
}
if ($setting['Value'] == $scriptSettings[$settingName]) {
// Not changed
continue;
}
$newSettings[$settingName] = $setting['Value'];
settype($newSettings[$settingName], gettype($scriptSettings[$settingName]));
}
$success = $this->applyNewScriptSettings($newSettings, $player);
if ($success) {
$this->maniaControl->getChat()->sendSuccess('Script Settings saved!', $player);
} else {
$this->maniaControl->getChat()->sendError('Script Settings Saving failed!', $player);
}
// Reopen the Menu
$this->maniaControl->getConfigurator()->showMenu($player, $this);
}
/**
* Apply the Array of new Script Settings
*
* @param array $newSettings
* @param Player $player
* @return bool
*/
private function applyNewScriptSettings(array $newSettings, Player $player) {
if (empty($newSettings)) {
return true;
}
$this->maniaControl->getClient()->setModeScriptSettings($newSettings);
// Save Settings into Database
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$query = "INSERT INTO `" . self::TABLE_SCRIPT_SETTINGS . "` (
`serverIndex`,
`settingName`,
`settingValue`
) VALUES (
?, ?, ?
) ON DUPLICATE KEY UPDATE
`settingValue` = VALUES(`settingValue`);";
$statement = $mysqli->prepare($query);
if ($mysqli->error) {
trigger_error($mysqli->error);
return false;
}
$settingName = null;
$settingValue = null;
$statement->bind_param('iss', $this->maniaControl->getServer()->index, $settingName, $settingValue);
// Notifications
$settingsCount = count($newSettings);
$settingIndex = 0;
$title = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($player);
$chatMessage = '$ff0' . $title . ' ' . $player->getEscapedNickname() . ' set ScriptSetting' . ($settingsCount > 1 ? 's' : '') . ' ';
foreach ($newSettings as $setting => $value) {
$chatMessage .= '$<' . '$fff' . preg_replace('/^S_/', '', $setting) . '$z$s$ff0 ';
$chatMessage .= 'to $fff' . $this->parseSettingValue($value) . '$>';
if ($settingIndex <= $settingsCount - 2) {
$chatMessage .= ', ';
}
// Add To Database
$settingName = $setting;
$settingValue = $value;
$statement->execute();
if ($statement->error) {
trigger_error($statement->error);
}
// Trigger own callback
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_SCRIPTSETTING_CHANGED, $setting, $value);
$settingIndex++;
}
$statement->close();
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_SCRIPTSETTINGS_CHANGED);
$chatMessage .= '!';
$this->maniaControl->getChat()->sendInformation($chatMessage);
Logger::logInfo($chatMessage, true);
return true;
}
/**
* Parse the Setting Value to a String Representation
*
* @param mixed $value
* @return string
*/
private function parseSettingValue($value) {
if (is_bool($value)) {
return ($value ? 'True' : 'False');
}
return (string)$value;
}
}

56
core/Database/Config.php Normal file
View File

@ -0,0 +1,56 @@
<?php
namespace ManiaControl\Database;
/**
* Model Class holding the Database Config
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class Config {
/*
* Public properties
*/
public $host = null;
public $port = null;
public $user = null;
public $pass = null;
public $name = null;
/**
* Create a new Database Config Instance
*
* @param string $host
* @param string $port
* @param string $user
* @param string $pass
* @param string $name
*/
public function __construct($host = null, $port = null, $user = null, $pass = null, $name = null) {
$this->host = (string)$host;
$this->port = (int)$port;
$this->user = (string)$user;
$this->pass = (string)$pass;
$this->name = (string)$name;
}
/**
* Validate the Config Data
*
* @return bool
*/
public function validate() {
if (!$this->host || !$this->port || !$this->user || !$this->pass || !$this->name) {
return false;
}
if ($this->user === 'mysql_user' || $this->pass === 'mysql_password') {
return false;
}
if ($this->name === 'database_name') {
return false;
}
return true;
}
}

235
core/Database/Database.php Normal file
View File

@ -0,0 +1,235 @@
<?php
namespace ManiaControl\Database;
use ManiaControl\Callbacks\TimerListener;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
/**
* Database Connection Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class Database implements TimerListener {
/*
* Public properties
*/
/** @var \mysqli $mysqli */
/** @deprecated see getMysqli() */
public $mysqli = null;
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/** @var Config $config */
private $config = null;
/** @var MigrationHelper $migrationHelper */
private $migrationHelper = null;
/**
* Construct a new Database Connection
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Enable mysqli Reconnect
ini_set('mysqli.reconnect', 'on');
// Open database connection
$this->loadConfig();
$this->mysqli = @new \mysqli($this->config->host, $this->config->user, $this->config->pass, null, $this->config->port);
if ($connectError = $this->getMysqli()->connect_error) {
$message = "Couldn't connect to Database: '{$connectError}'";
$this->maniaControl->quit($message, true);
}
$this->getMysqli()->set_charset("utf8");
$this->initDatabase();
$this->optimizeTables();
// Register Method which checks the Database Connection every 5 seconds
$this->maniaControl->getTimerManager()->registerTimerListening($this, 'checkConnection', 5000);
// Children
$this->migrationHelper = new MigrationHelper($maniaControl);
}
/**
* Load the Database Config
*/
private function loadConfig() {
$databaseElements = $this->maniaControl->getConfig()->xpath('database');
if (!$databaseElements) {
$this->maniaControl->quit('No Database configured!', true);
}
$databaseElement = $databaseElements[0];
// Host
$hostElements = $databaseElement->xpath('host');
if (!$hostElements) {
$this->maniaControl->quit("Invalid database configuration (Host).", true);
}
$host = (string)$hostElements[0];
// Port
$portElements = $databaseElement->xpath('port');
if (!$portElements) {
$this->maniaControl->quit("Invalid database configuration (Port).", true);
}
$port = (string)$portElements[0];
// User
$userElements = $databaseElement->xpath('user');
if (!$userElements) {
$this->maniaControl->quit("Invalid database configuration (User).", true);
}
$user = (string)$userElements[0];
// Pass
$passElements = $databaseElement->xpath('pass');
if (!$passElements) {
$this->maniaControl->quit("Invalid database configuration (Pass).", true);
}
$pass = (string)$passElements[0];
// Name
$nameElements = $databaseElement->xpath('name');
if (!$nameElements) {
$nameElements = $databaseElement->xpath('db_name');
}
if (!$nameElements) {
$this->maniaControl->quit("Invalid database configuration (Name).", true);
}
$name = (string)$nameElements[0];
// Create config object
$config = new Config($host, $port, $user, $pass, $name);
if (!$config->validate()) {
$this->maniaControl->quit("Your config file doesn't seem to be maintained properly. Please check the database configuration again!", true);
}
$this->config = $config;
}
/**
* Connect to the defined Database
*
* @return bool
*/
private function initDatabase() {
// Try to connect
$result = $this->getMysqli()->select_db($this->config->name);
if ($result) {
return true;
}
Logger::logInfo("Database '{$this->config->name}' doesn't exist! Trying to create it...");
// Create database
$databaseQuery = "CREATE DATABASE " . $this->getMysqli()->escape_string($this->config->name) . ";";
$this->getMysqli()->query($databaseQuery);
if ($this->getMysqli()->error) {
$this->maniaControl->quit($this->getMysqli()->error, true);
return false;
}
// Connect to new database
$this->getMysqli()->select_db($this->config->name);
if ($error = $this->getMysqli()->error) {
$message = "Couldn't select database '{$this->config->name}'. {$error}";
$this->maniaControl->quit($message, true);
return false;
}
return true;
}
/**
* Optimize all existing Tables
*
* @return bool
*/
private function optimizeTables() {
$showQuery = 'SHOW TABLES;';
$result = $this->getMysqli()->query($showQuery);
if ($error = $this->getMysqli()->error) {
Logger::logError($error);
return false;
}
$count = $result->num_rows;
if ($count <= 0) {
$result->free();
return true;
}
$optimizeQuery = 'OPTIMIZE TABLE ';
$index = 0;
while ($row = $result->fetch_row()) {
$tableName = $row[0];
$optimizeQuery .= "`{$tableName}`";
if ($index < $count - 1) {
$optimizeQuery .= ',';
}
$index++;
}
$result->free();
$optimizeQuery .= ';';
$this->getMysqli()->query($optimizeQuery);
if ($error = $this->getMysqli()->error) {
Logger::logError($error);
return false;
}
return true;
}
/**
* Return the mysqli instance
*
* @return \mysqli
*/
public function getMysqli() {
return $this->mysqli;
}
/**
* Return the database config
*
* @return Config
*/
public function getConfig() {
return $this->config;
}
/**
* Return the migration helper
*
* @return MigrationHelper
*/
public function getMigrationHelper() {
return $this->migrationHelper;
}
/**
* Check whether the Database Connection is still open
*/
public function checkConnection() {
if (!$this->getMysqli()
|| !$this->getMysqli()->ping()
) {
$this->maniaControl->quit('The MySQL Server has gone away!', true);
}
}
/**
* Destruct Database Connection
*/
public function __destruct() {
if ($this->getMysqli() && !$this->getMysqli()->connect_error) {
$this->getMysqli()->close();
}
}
}

View File

@ -0,0 +1,65 @@
<?php
namespace ManiaControl\Database;
use ManiaControl\ManiaControl;
use ManiaControl\Settings\SettingManager;
use ManiaControl\Utils\ClassUtil;
/**
* Database Migration Assistant
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class MigrationHelper {
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/**
* Construct a new migration helper instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
}
/**
* Transfer the Settings of the given Class to a new One
*
* @param mixed $sourceClass
* @param mixed $targetClass
* @return bool
*/
public function transferSettings($sourceClass, $targetClass) {
$sourceClass = ClassUtil::getClass($sourceClass);
$targetClass = ClassUtil::getClass($targetClass);
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$query = "INSERT IGNORE INTO `" . SettingManager::TABLE_SETTINGS . "`
(`class`, `setting`, `type`, `value`, `default`)
SELECT ?, `setting`, `type`, `value`, `default`
FROM `" . SettingManager::TABLE_SETTINGS . "`
WHERE `class` = ?;";
$statement = $mysqli->prepare($query);
if ($mysqli->error) {
trigger_error($mysqli->error);
return false;
}
$statement->bind_param('ss', $targetClass, $sourceClass);
$success = $statement->execute();
if ($statement->error) {
trigger_error($statement->error);
$statement->close();
return false;
}
$statement->close();
return $success;
}
}

476
core/ErrorHandler.php Normal file
View File

@ -0,0 +1,476 @@
<?php
namespace ManiaControl;
use ManiaControl\Plugins\PluginManager;
use ManiaControl\Update\UpdateManager;
use ManiaControl\Utils\Formatter;
use ManiaControl\Utils\WebReader;
use Maniaplanet\DedicatedServer\Xmlrpc\TransportException;
/**
* Error and Exception Manager Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class ErrorHandler {
/*
* Constants
*/
const MC_DEBUG_NOTICE = 'ManiaControl.DebugNotice';
const SETTING_RESTART_ON_EXCEPTION = 'Automatically restart on Exceptions';
const LOG_SUPPRESSED_ERRORS = false;
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
private $handlingError = null;
/**
* Construct a new error handler instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
set_error_handler(array(&$this, 'handleError'), -1);
set_exception_handler(array(&$this, 'handleException'));
register_shutdown_function(array(&$this, 'handleShutdown'));
}
/**
* Initialize error handler features
*/
public function init() {
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_RESTART_ON_EXCEPTION, true);
}
/**
* Trigger a Debug Notice to the ManiaControl Website
*
* @param string $message
*/
public function triggerDebugNotice($message) {
$this->handleError(self::MC_DEBUG_NOTICE, $message);
}
/**
* ManiaControl Error Handler
*
* @param int $errorNumber
* @param string $errorString
* @param string $errorFile
* @param int $errorLine
* @param array $errorContext
* @param bool $onShutdown
* @return bool
*/
public function handleError($errorNumber, $errorString, $errorFile = null, $errorLine = -1, array $errorContext = array(), $onShutdown = false) {
$suppressed = (error_reporting() === 0);
if ($suppressed && !self::LOG_SUPPRESSED_ERRORS) {
return false;
}
if (!$this->handlingError) {
// Reset error handler for safety
$this->handlingError = true;
set_error_handler(array(&$this, 'handleError'), -1);
}
// Build log message
$errorTag = $this->getErrorTag($errorNumber);
$isUserError = self::isUserErrorNumber($errorNumber);
$isFatalError = self::isFatalError($errorNumber);
$traceString = null;
$sourceClass = null;
$traceSourceClass = null;
$fileLine = null;
$message = $errorTag . ': ' . $errorString;
if (!$onShutdown) {
$traceString = $this->parseBackTrace(array_slice(debug_backtrace(), 1), $traceSourceClass);
}
if ($errorFile) {
$fileLine = $errorFile . ': ' . $errorLine;
$sourceClass = $this->getSourceClass($errorFile);
}
if (!$sourceClass && $traceSourceClass) {
$sourceClass = $traceSourceClass;
}
$logMessage = $message . PHP_EOL . 'File&Line: ' . $fileLine;
if (!$isUserError && $traceString) {
$logMessage .= PHP_EOL . 'Trace: ' . PHP_EOL . $traceString;
}
Logger::log($logMessage);
if (!DEV_MODE && !$isUserError && !$suppressed) {
// Report error
$report = array();
$report['Type'] = 'Error';
$report['Message'] = $message;
if ($fileLine) {
$report['FileLine'] = self::stripBaseDir($fileLine);
}
if ($sourceClass) {
$report['SourceClass'] = $sourceClass;
$pluginId = PluginManager::getPluginId($sourceClass);
if ($pluginId > 0) {
$report['PluginId'] = $pluginId;
if ($isFatalError) {
$this->maniaControl->getPluginManager()->deactivatePlugin($sourceClass);
}
}
}
if ($traceString) {
$report['Backtrace'] = $traceString;
}
$report['OperatingSystem'] = php_uname();
$report['PHPVersion'] = phpversion();
if ($this->maniaControl->getServer()) {
$report['ServerLogin'] = $this->maniaControl->getServer()->login;
}
if ($this->maniaControl->getSettingManager() && $this->maniaControl->getUpdateManager()) {
$report['UpdateChannel'] = $this->maniaControl->getSettingManager()->getSettingValue($this->maniaControl->getUpdateManager(), UpdateManager::SETTING_UPDATECHECK_CHANNEL);
$report['ManiaControlVersion'] = ManiaControl::VERSION . ' ' . $this->maniaControl->getUpdateManager()->getBuildDate();
} else {
$report['ManiaControlVersion'] = ManiaControl::VERSION;
}
$json = json_encode(Formatter::utf8($report));
$info = base64_encode($json);
$url = ManiaControl::URL_WEBSERVICE . 'errorreport?error=' . urlencode($info);
$response = WebReader::getUrl($url);
$content = $response->getContent();
$success = json_decode($content);
if ($success) {
Logger::log('Error-Report successful!');
} else {
Logger::log('Error-Report failed! ' . print_r($content, true));
}
}
if ($isFatalError) {
$this->maniaControl->quit('Quitting ManiaControl after Fatal Error.');
}
// Disable safety state
$this->handlingError = false;
return false;
}
/**
* Get the Prefix for the given Error Level
*
* @param int $errorLevel
* @return string
*/
public function getErrorTag($errorLevel) {
switch ($errorLevel) {
case E_ERROR:
return '[PHP ERROR]';
case E_WARNING:
return '[PHP WARNING]';
case E_PARSE:
return '[PHP PARSE ERROR]';
case E_NOTICE:
return '[PHP NOTICE]';
case E_CORE_ERROR:
return '[PHP CORE ERROR]';
case E_COMPILE_ERROR:
return '[PHP COMPILE ERROR]';
case E_USER_ERROR:
return '[ManiaControl ERROR]';
case E_USER_WARNING:
return '[ManiaControl WARNING]';
case E_USER_NOTICE:
return '[ManiaControl NOTICE]';
case E_RECOVERABLE_ERROR:
return '[PHP RECOVERABLE ERROR]';
case self::MC_DEBUG_NOTICE:
return '[ManiaControl DEBUG]';
}
return "[PHP ERROR '{$errorLevel}']";
}
/**
* Check if the given Error Number is a User Error
*
* @param int $errorNumber
* @return bool
*/
private static function isUserErrorNumber($errorNumber) {
return ($errorNumber & E_USER_ERROR || $errorNumber & E_USER_WARNING || $errorNumber & E_USER_NOTICE
|| $errorNumber & E_USER_DEPRECATED);
}
/**
* Test whether the given Error Number represents a Fatal Error
*
* @param int $errorNumber
* @return bool
*/
public static function isFatalError($errorNumber) {
$fatalError = (E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR);
return ($errorNumber & $fatalError);
}
/**
* Parse the Debug Backtrace into a String for the Error Report
*
* @param array $backtrace
* @param string $sourceClass
* @return string
*/
private function parseBackTrace(array $backtrace, &$sourceClass = null) {
$traceString = '';
$stepCount = 0;
foreach ($backtrace as $traceStep) {
$skipStep = $this->shouldSkipTraceStep($traceStep);
$traceString .= '#' . $stepCount . ': ';
if (isset($traceStep['class'])) {
if (!$sourceClass && !$skipStep && !$this->isIgnoredSourceClass($traceStep['class'])) {
$sourceClass = $traceStep['class'];
}
$traceString .= $traceStep['class'];
}
if (isset($traceStep['type'])) {
$traceString .= $traceStep['type'];
}
if (isset($traceStep['function'])) {
$traceString .= $traceStep['function'] . '(';
if (isset($traceStep['args']) && !$skipStep) {
$traceString .= $this->parseArgumentsArray($traceStep['args']);
}
$traceString .= ')';
}
if (isset($traceStep['file']) && !$skipStep) {
$traceString .= ' in File ';
$traceString .= self::stripBaseDir($traceStep['file']);
}
if (isset($traceStep['line']) && !$skipStep) {
$traceString .= ' on Line ';
$traceString .= $traceStep['line'];
}
$traceString .= PHP_EOL;
if (strlen($traceString) > 2500) {
// Too long...
$traceString .= '...';
break;
}
$stepCount++;
}
return $traceString;
}
/**
* Check if the given Trace Step should be skipped
*
* @param array $traceStep
* @return bool
*/
private function shouldSkipTraceStep(array $traceStep) {
if (isset($traceStep['class'])) {
$skippedClasses = array('Symfony\\Component\\EventDispatcher\\EventDispatcher', 'cURL\\Request');
foreach ($skippedClasses as $skippedClass) {
if ($traceStep['class'] === $skippedClass) {
return true;
}
}
}
if (isset($traceStep['file'])) {
$skippedFiles = array('Symfony', 'curl-easy');
foreach ($skippedFiles as $skippedFile) {
if (strpos($traceStep['file'], $skippedFile)) {
return true;
}
}
}
return false;
}
/**
* Check if the given Class Name should be ignored as possible Error Source Class
*
* @param string $class
* @return bool
*/
private function isIgnoredSourceClass($class) {
$ignoredClasses = array('Maniaplanet\\', '\\ErrorHandler');
foreach ($ignoredClasses as $ignoredClass) {
if (strpos($class, $ignoredClass) !== false) {
return true;
}
}
return false;
}
/**
* Build a string from an arguments array
*
* @param array $args
* @return string
*/
private function parseArgumentsArray(array $args) {
$string = '';
$argsCount = count($args);
foreach ($args as $index => $arg) {
if (is_object($arg)) {
$string .= 'object(' . get_class($arg) . ')';
} else if (is_array($arg)) {
$string .= 'array(' . $this->parseArgumentsArray($arg) . ')';
} else {
$type = gettype($arg);
$string .= $type . '(';
if (is_string($arg)) {
$param = $arg;
if (strlen($param) > 40) {
$param = substr($param, 0, 40) . '..';
}
$string .= print_r($param, true);
} else {
$string .= print_r($arg, true);
}
$string .= ')';
}
if ($index < $argsCount - 1) {
$string .= ', ';
}
if (strlen($string) > 150) {
// Too long...
$string .= '...';
break;
}
}
return $string;
}
/**
* Strip the ManiaControl path from the given path to ensure privacy
*
* @param string $path
* @return string
*/
private static function stripBaseDir($path) {
return str_replace(MANIACONTROL_PATH, '', $path);
}
/**
* Get the source class via the error file
*
* @param string $errorFile
* @return string
*/
private function getSourceClass($errorFile) {
if (!$errorFile) {
return null;
}
$filePath = substr($errorFile, strlen(MANIACONTROL_PATH));
$filePath = str_replace('plugins' . DIRECTORY_SEPARATOR, '', $filePath);
$filePath = str_replace('core' . DIRECTORY_SEPARATOR, 'ManiaControl\\', $filePath);
$className = str_replace('.php', '', $filePath);
$className = str_replace(DIRECTORY_SEPARATOR, '\\', $className);
if (!class_exists($className, false)) {
return null;
}
return $className;
}
/**
* Handle PHP Process Shutdown
*/
public function handleShutdown() {
// Check if the Shutdown was caused by a Fatal Error and report it
$error = error_get_last();
if ($error && self::isFatalError($error['type'])) {
$this->handleError($error['type'], $error['message'], $error['file'], $error['line'], array(), true);
}
$this->maniaControl->quit('Quitting ManiaControl!');
}
/**
* ManiaControl Exception Handler
*
* @param \Exception $exception
* @param bool $shutdown
*/
public function handleException(\Exception $exception, $shutdown = true) {
$message = "[ManiaControl EXCEPTION]: {$exception->getMessage()}";
$exceptionClass = get_class($exception);
$sourceClass = null;
$traceString = $this->parseBackTrace($exception->getTrace(), $sourceClass);
$logMessage = $message . PHP_EOL . 'Class: ' . $exceptionClass . PHP_EOL . 'Trace:' . PHP_EOL . $traceString;
Logger::log($logMessage);
if (!DEV_MODE) {
$report = array();
$report['Type'] = 'Exception';
$report['Message'] = $message;
$report['Class'] = $exceptionClass;
$report['FileLine'] = self::stripBaseDir($exception->getFile()) . ': ' . $exception->getLine();
$report['SourceClass'] = $sourceClass;
$report['PluginId'] = PluginManager::getPluginId($sourceClass);
$report['Backtrace'] = $traceString;
$report['OperatingSystem'] = php_uname();
$report['PHPVersion'] = phpversion();
if ($server = $this->maniaControl->getServer()) {
$report['ServerLogin'] = $server->login;
}
if (($settingManager = $this->maniaControl->getSettingManager()) && ($updateManager = $this->maniaControl->getUpdateManager())) {
$report['UpdateChannel'] = $settingManager->getSettingValue($updateManager, $updateManager::SETTING_UPDATECHECK_CHANNEL);
$report['ManiaControlVersion'] = ManiaControl::VERSION . ' #' . $updateManager->getBuildDate();
} else {
$report['ManiaControlVersion'] = ManiaControl::VERSION;
}
$errorReport = json_encode(Formatter::utf8($report));
$url = ManiaControl::URL_WEBSERVICE . 'errorreport';
$response = WebReader::postUrl($url, $errorReport);
$content = $response->getContent();
$success = json_decode($content);
if ($success) {
Logger::log('Exception successfully reported!');
} else {
Logger::log('Exception-Report failed! ' . print_r($content, true));
}
}
if ($shutdown) {
if ($this->shouldRestart()) {
$this->maniaControl->restart();
}
try {
$this->maniaControl->quit('Quitting ManiaControl after Exception.');
} catch (TransportException $e) {
}
}
}
/**
* Test if ManiaControl should restart automatically
*
* @return bool
*/
private function shouldRestart() {
if (!$this->maniaControl || !$this->maniaControl->getSettingManager() || DEV_MODE) {
return false;
}
$setting = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_RESTART_ON_EXCEPTION, true);
return $setting;
}
}

View File

@ -0,0 +1,196 @@
<?php
namespace ManiaControl\Files;
use cURL\Event;
use cURL\Request;
use ManiaControl\ManiaControl;
/**
* Asynchronous File Reader
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class AsynchronousFileReader {
/*
* Constants
*/
const CONTENT_TYPE_JSON = 'application/json';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/** @var Request[] $requests */
private $requests = array();
/**
* Construct a new Asynchronous File Reader Instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
}
public static function newRequestTest($url) {
$request = new Request($url);
$request->getOptions()->set(CURLOPT_TIMEOUT, 60)->set(CURLOPT_HEADER, false) // don't display response header
->set(CURLOPT_CRLF, true) // linux line feed
->set(CURLOPT_ENCODING, '') // accept encoding
->set(CURLOPT_USERAGENT, 'ManiaControl v' . ManiaControl::VERSION) // user-agent
->set(CURLOPT_RETURNTRANSFER, true); // return instead of output content
return $request;
}
/**
* Append available Data of active Requests
*/
public function appendData() {
foreach ($this->requests as $key => $request) {
if ($request->socketPerform()) {
$request->socketSelect();
} else {
unset($this->requests[$key]);
}
}
}
/**
* Load a Remote File
*
* @param string $url
* @param callable $function
* @param string $contentType
* @param int $keepAlive
*/
public function loadFile($url, callable $function, $contentType = 'UTF-8', $keepAlive = 0) {
$headers = array();
array_push($headers, 'Content-Type: ' . $contentType);
if ($keepAlive) {
array_push($headers, 'Keep-Alive: ' . $keepAlive);
array_push($headers, 'Connection: Keep-Alive');
}
$request = $this->newRequest($url);
$request->getOptions()->set(CURLOPT_AUTOREFERER, true) // accept link reference
->set(CURLOPT_HTTPHEADER, $headers); // headers
$request->addListener('complete', function (Event $event) use (&$function) {
$error = null;
$content = null;
if ($event->response->hasError()) {
$error = $event->response->getError()->getMessage();
} else {
$content = $event->response->getContent();
}
call_user_func($function, $content, $error);
});
$this->addRequest($request);
}
/**
* Create a new cURL Request for the given URL
*
* @param string $url
* @return Request
*/
protected function newRequest($url) {
$request = new Request($url);
$request->getOptions()->set(CURLOPT_TIMEOUT, 60)->set(CURLOPT_HEADER, false) // don't display response header
->set(CURLOPT_CRLF, true) // linux line feed
->set(CURLOPT_ENCODING, '') // accept encoding
->set(CURLOPT_USERAGENT, 'ManiaControl v' . ManiaControl::VERSION) // user-agent
->set(CURLOPT_RETURNTRANSFER, true); // return instead of output content
return $request;
}
//TODO remove, they are just for testing dedimania
/**
* Add a Request to the queue
*
* @param Request $request
*/
protected function addRequest(Request $request) {
array_push($this->requests, $request);
}
public function postDataTest(Request $request, $url, callable $function, $content, $compression = false, $contentType = 'text/xml; charset=UTF-8;') {
$headers = array();
array_push($headers, 'Content-Type: ' . $contentType);
array_push($headers, 'Keep-Alive: timeout=600, max=2000');
array_push($headers, 'Connection: Keep-Alive');
$content = str_replace(array("\r", "\n"), '', $content);
if ($compression) {
$content = zlib_encode($content, 31);
array_push($headers, 'Content-Encoding: gzip');
}
$request->getOptions()->set(CURLOPT_POST, true) // post method
->set(CURLOPT_POSTFIELDS, $content) // post content field
->set(CURLOPT_HTTPHEADER, $headers) // headers
;
$request->addListener('complete', function (Event $event) use (&$function) {
$error = null;
$content = null;
if ($event->response->hasError()) {
$error = $event->response->getError()->getMessage();
} else {
$content = $event->response->getContent();
}
call_user_func($function, $content, $error);
});
$this->addRequest($request);
}
/**
* Send Data via POST Method
*
* @param string $url
* @param callable $function
* @param string $content
* @param bool $compression
* @param string $contentType
*/
public function postData($url, callable $function, $content, $compression = false, $contentType = 'text/xml; charset=UTF-8;') {
$headers = array();
array_push($headers, 'Content-Type: ' . $contentType);
array_push($headers, 'Keep-Alive: timeout=600, max=2000');
array_push($headers, 'Connection: Keep-Alive');
$content = str_replace(array("\r", "\n"), '', $content);
if ($compression) {
$content = zlib_encode($content, 31);
array_push($headers, 'Content-Encoding: gzip');
}
$request = $this->newRequest($url);
$request->getOptions()->set(CURLOPT_POST, true) // post method
->set(CURLOPT_POSTFIELDS, $content) // post content field
->set(CURLOPT_HTTPHEADER, $headers) // headers
;
$request->addListener('complete', function (Event $event) use (&$function) {
$error = null;
$content = null;
if ($event->response->hasError()) {
$error = $event->response->getError()->getMessage();
} else {
$content = $event->response->getContent();
}
call_user_func($function, $content, $error);
});
$this->addRequest($request);
}
}

131
core/Files/BackupUtil.php Normal file
View File

@ -0,0 +1,131 @@
<?php
namespace ManiaControl\Files;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
/**
* Backup Utility Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
abstract class BackupUtil {
/**
* Perform a Full Backup of ManiaControl
*
* @return bool
*/
public static function performFullBackup() {
$backupFolder = self::getBackupFolder();
if (!$backupFolder) {
return false;
}
$backupFileName = $backupFolder . 'backup_' . ManiaControl::VERSION . '_' . date('y-m-d_H-i') . '_' . time() . '.zip';
$backupZip = new \ZipArchive();
if ($backupZip->open($backupFileName, \ZipArchive::CREATE) !== true) {
Logger::logError("Couldn't create backup zip!");
return false;
}
$excludes = array();
$baseFileNames = array('configs', 'core', 'plugins', 'ManiaControl.php');
$pathInfo = pathInfo(MANIACONTROL_PATH);
$parentPath = $pathInfo['dirname'] . DIRECTORY_SEPARATOR;
$dirName = $pathInfo['basename'];
$backupZip->addEmptyDir($dirName);
self::zipDirectory($backupZip, MANIACONTROL_PATH, strlen($parentPath), $excludes, $baseFileNames);
return $backupZip->close();
}
/**
* Get the Backup Folder Path and create it if necessary
*
* @return string|bool
*/
private static function getBackupFolder() {
$backupFolder = MANIACONTROL_PATH . 'backup' . DIRECTORY_SEPARATOR;
if (!is_dir($backupFolder) && !mkdir($backupFolder)) {
Logger::logError("Couldn't create backup folder!");
return false;
}
if (!is_writeable($backupFolder)) {
Logger::logError("ManiaControl doesn't have the necessary write rights for the backup folder!");
return false;
}
return $backupFolder;
}
/**
* Add a Directory to the ZipArchive
*
* @param \ZipArchive $zipArchive
* @param string $folderName
* @param int $prefixLength
* @param array $excludes
* @param array $baseFileNames
* @return bool
*/
private static function zipDirectory(\ZipArchive &$zipArchive, $folderName, $prefixLength, array $excludes = array(), array $baseFileNames = array()) {
$folderHandle = opendir($folderName);
if (!is_resource($folderHandle)) {
Logger::logError("Couldn't open folder '{$folderName}' for backup!");
return false;
}
$useBaseFileNames = !empty($baseFileNames);
while (false !== ($file = readdir($folderHandle))) {
if (substr($file, 0, 1) === '.') {
// Skip such .files
continue;
}
if (in_array($file, $excludes)) {
// Excluded
continue;
}
if ($useBaseFileNames && !in_array($file, $baseFileNames)) {
// Not one of the base files
continue;
}
$filePath = $folderName . DIRECTORY_SEPARATOR . $file;
$localPath = substr($filePath, $prefixLength);
if (is_file($filePath)) {
$zipArchive->addFile($filePath, $localPath);
continue;
}
if (is_dir($filePath)) {
$zipArchive->addEmptyDir($localPath);
self::zipDirectory($zipArchive, $filePath, $prefixLength, $excludes);
continue;
}
}
closedir($folderHandle);
return true;
}
/**
* Perform a Backup of the Plugins
*
* @return bool
*/
public static function performPluginsBackup() {
$backupFolder = self::getBackupFolder();
if (!$backupFolder) {
return false;
}
$backupFileName = $backupFolder . 'backup_plugins_' . ManiaControl::VERSION . date('y-m-d_H-i') . '_' . time() . '.zip';
$backupZip = new \ZipArchive();
if ($backupZip->open($backupFileName, \ZipArchive::CREATE) !== true) {
Logger::logError("Couldn't create backup zip!");
return false;
}
$directory = MANIACONTROL_PATH . 'plugins';
$pathInfo = pathInfo($directory);
$parentPath = $pathInfo['dirname'] . DIRECTORY_SEPARATOR;
$dirName = $pathInfo['basename'];
$backupZip->addEmptyDir($dirName);
self::zipDirectory($backupZip, $directory, strlen($parentPath));
return $backupZip->close();
}
}

210
core/Files/FileUtil.php Normal file
View File

@ -0,0 +1,210 @@
<?php
namespace ManiaControl\Files;
use ManiaControl\Logger;
use ManiaControl\Utils\Formatter;
use ManiaControl\Utils\WebReader;
/**
* Files Utility Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
abstract class FileUtil {
/**
* @deprecated
* @see \ManiaControl\Utils\WebReader::loadUrl()
*/
public static function loadFile($url) {
$response = WebReader::getUrl($url);
return $response->getContent();
}
/**
* Load Config XML File
*
* @param string $fileName
* @return \SimpleXMLElement
*/
public static function loadConfig($fileName) {
$fileLocation = MANIACONTROL_PATH . 'configs' . DIRECTORY_SEPARATOR . $fileName;
if (!file_exists($fileLocation)) {
Logger::log("Config file doesn't exist! ({$fileName})");
return null;
}
if (!is_readable($fileLocation)) {
Logger::log("Config file isn't readable! Please check the file permissions. ({$fileName})");
return null;
}
$configXml = @simplexml_load_file($fileLocation);
if (!$configXml) {
Logger::log("Config file isn't maintained properly! ({$fileName})");
return null;
}
return $configXml;
}
/**
* Return file name cleared from special characters
*
* @param string $fileName
* @return string
*/
public static function getClearedFileName($fileName) {
$fileName = Formatter::stripCodes($fileName);
$fileName = Formatter::utf8($fileName);
$fileName = preg_replace('/[^0-9A-Za-z\-\+\.\_\ ]/', null, $fileName);
$fileName = preg_replace('/ /', '_', $fileName);
return $fileName;
}
/**
* Delete the temporary folder if it's empty
*
* @return bool
*/
public static function deleteTempFolder() {
return self::deleteFolder(self::getTempFolder());
}
/**
* Delete the given folder if it's empty
*
* @param string $folderPath
* @param bool $onlyIfEmpty
* @return bool
*/
public static function deleteFolder($folderPath, $onlyIfEmpty = true) {
if ($onlyIfEmpty && !self::isFolderEmpty($folderPath)) {
return false;
}
return rmdir($folderPath);
}
/**
* Check if the given folder is empty
*
* @param string $folderPath
* @return bool
*/
public static function isFolderEmpty($folderPath) {
if (!is_readable($folderPath) || !is_dir($folderPath)) {
return false;
}
$files = scandir($folderPath);
return (count($files) <= 2);
}
/**
* Get the temporary folder and create it if necessary
*
* @return string|bool
*/
public static function getTempFolder() {
$tempFolder = MANIACONTROL_PATH . 'temp' . DIRECTORY_SEPARATOR;
if (!is_dir($tempFolder) && !mkdir($tempFolder)) {
trigger_error("Couldn't create the temp folder!");
return false;
}
if (!is_writeable($tempFolder)) {
trigger_error("ManiaControl doesn't have the necessary write rights for the temp folder!");
return false;
}
return $tempFolder;
}
/**
* Check if ManiaControl has sufficient Access to write to Files in the given Directories
*
* @param mixed $directories
* @return bool
*/
public static function checkWritePermissions($directories) {
if (!is_array($directories)) {
$directories = array($directories);
}
foreach ($directories as $directory) {
$dir = new \RecursiveDirectoryIterator(MANIACONTROL_PATH . $directory);
foreach (new \RecursiveIteratorIterator($dir) as $fileName => $file) {
if (substr($fileName, 0, 1) === '.') {
continue;
}
if (!is_writable($fileName)) {
Logger::log("Write access missing for file '{$fileName}'!");
return false;
}
}
}
return true;
}
/**
* Clean the given directory by deleting old files
*
* @param string $directory
* @param float $maxFileAgeInDays
* @param bool $recursive
* @return bool
*/
public static function cleanDirectory($directory, $maxFileAgeInDays = 10., $recursive = false) {
if (!$directory || !is_dir($directory) || !is_readable($directory)) {
return false;
}
$dirHandle = opendir($directory);
if (!is_resource($dirHandle)) {
return false;
}
$directory = self::appendDirectorySeparator($directory);
$time = time();
while ($fileName = readdir($dirHandle)) {
$filePath = $directory . $fileName;
if (!is_readable($filePath)) {
continue;
}
if (is_dir($filePath) && $recursive) {
// Directory
self::cleanDirectory($filePath . DIRECTORY_SEPARATOR, $maxFileAgeInDays, $recursive);
} else if (is_file($filePath)) {
// File
if (!is_writable($filePath)) {
continue;
}
$fileModTime = filemtime($filePath);
$timeDeltaDays = ($time - $fileModTime) / (24. * 3600.);
if ($timeDeltaDays > $maxFileAgeInDays) {
unlink($filePath);
}
}
}
closedir($dirHandle);
return true;
}
/**
* Append the directory separator to the given path if necessary
*
* @param string $path
* @return string
*/
public static function appendDirectorySeparator($path) {
if (substr($path, -1, 1) !== DIRECTORY_SEPARATOR) {
$path .= DIRECTORY_SEPARATOR;
}
return $path;
}
/**
* Check whether the given file name is a PHP file
*
* @param string $fileName
* @return bool
*/
public static function isPhpFileName($fileName) {
$extension = substr($fileName, -4);
return (strtolower($extension) === '.php');
}
}

134
core/Logger.php Normal file
View File

@ -0,0 +1,134 @@
<?php
namespace ManiaControl;
use ManiaControl\Files\FileUtil;
use ManiaControl\Utils\Formatter;
/**
* ManiaControl Logger Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
abstract class Logger {
/**
* Setup the logging mechanism
*/
public static function setup() {
self::setupErrorLogFileName();
self::cleanLogsFolder();
}
/**
* Set the error log file name
*/
private static function setupErrorLogFileName() {
$logsFolder = self::getLogsFolder();
if ($logsFolder) {
$logFileName = $logsFolder . 'ManiaControl';
if (!defined('LOG_NAME_USE_DATE') || LOG_NAME_USE_DATE) {
$logFileName .= '_' . date('Y-m-d');
}
if (!defined('LOG_NAME_USE_PID') || LOG_NAME_USE_PID) {
$logFileName .= '_' . getmypid();
}
$logFileName .= '.log';
ini_set('error_log', $logFileName);
}
}
/**
* Get the logs folder and create it if necessary
*
* @return string
*/
public static function getLogsFolder() {
$logsFolder = MANIACONTROL_PATH . 'logs' . DIRECTORY_SEPARATOR;
if (!is_dir($logsFolder) && !mkdir($logsFolder)) {
self::logError("Couldn't create the logs folder!");
return null;
}
if (!is_writeable($logsFolder)) {
self::logError("ManiaControl doesn't have the necessary write rights for the logs folder!");
return null;
}
return $logsFolder;
}
/**
* Delete old ManiaControl log files
*
* @return bool
*/
private static function cleanLogsFolder() {
$logsFolderPath = self::getLogsFolder();
return FileUtil::cleanDirectory($logsFolderPath);
}
/**
* Log and output the given Error message
*
* @param string $message
* @param bool $stripCodes
* @param bool $eol
*/
public static function logError($message, $stripCodes = false, $eol = true) {
$message = '[ERROR] ' . $message;
self::log($message, $stripCodes, $eol);
}
/**
* Log and output the given message
*
* @param string $message
* @param bool $stripCodes
* @param bool $eol
*/
public static function log($message, $stripCodes = false, $eol = true) {
if ($stripCodes) {
$message = Formatter::stripCodes($message);
}
error_log($message);
self::output($message, $eol);
}
/**
* Echo the given message
*
* @param string $message
* @param bool $eol
*/
private static function output($message, $eol = true) {
if ($eol) {
$message = '[' . date('d-M-Y H:i:s e') . '] ' . $message . PHP_EOL;
}
echo $message;
}
/**
* Log and output the given Info message
*
* @param string $message
* @param bool $stripCodes
* @param bool $eol
*/
public static function logInfo($message, $stripCodes = false, $eol = true) {
$message = '[INFO] ' . $message;
self::log($message, $stripCodes, $eol);
}
/**
* Log and output the given Warning message
*
* @param string $message
* @param bool $stripCodes
* @param bool $eol
*/
public static function logWarning($message, $stripCodes = false, $eol = true) {
$message = '[WARNING] ' . $message;
self::log($message, $stripCodes, $eol);
}
}

636
core/ManiaControl.php Normal file
View File

@ -0,0 +1,636 @@
<?php
namespace ManiaControl;
use ManiaControl\Admin\ActionsMenu;
use ManiaControl\Admin\AuthenticationManager;
use ManiaControl\Bills\BillManager;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\CallbackManager;
use ManiaControl\Callbacks\Callbacks;
use ManiaControl\Callbacks\TimerListener;
use ManiaControl\Callbacks\TimerManager;
use ManiaControl\Commands\CommandListener;
use ManiaControl\Commands\CommandManager;
use ManiaControl\Configurator\Configurator;
use ManiaControl\Database\Database;
use ManiaControl\Files\AsynchronousFileReader;
use ManiaControl\Files\FileUtil;
use ManiaControl\Manialinks\ManialinkManager;
use ManiaControl\Maps\MapManager;
use ManiaControl\Players\Player;
use ManiaControl\Players\PlayerManager;
use ManiaControl\Plugins\PluginManager;
use ManiaControl\Server\Server;
use ManiaControl\Settings\SettingManager;
use ManiaControl\Statistics\StatisticManager;
use ManiaControl\Update\UpdateManager;
use ManiaControl\Utils\CommandLineHelper;
use ManiaControl\Utils\SystemUtil;
use Maniaplanet\DedicatedServer\Connection;
use Maniaplanet\DedicatedServer\Xmlrpc\AuthenticationException;
use Maniaplanet\DedicatedServer\Xmlrpc\TransportException;
/**
* ManiaControl Server Controller for ManiaPlanet Server
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class ManiaControl implements CallbackListener, CommandListener, TimerListener {
/*
* Constants
*/
const VERSION = '0.154';
const API_VERSION = '2013-04-16';
const MIN_DEDIVERSION = '2014-04-02_18_00';
const SCRIPT_TIMEOUT = 10;
const URL_WEBSERVICE = 'http://ws.maniacontrol.com/';
const SETTING_PERMISSION_SHUTDOWN = 'Shutdown ManiaControl';
const SETTING_PERMISSION_RESTART = 'Restart ManiaControl';
/*
* Public properties
*/
/** @var ActionsMenu $actionsMenu
* @deprecated see getActionsMenu()
*/
public $actionsMenu = null;
/** @var AuthenticationManager $authenticationManager
* @deprecated see getAuthenticationManager()
*/
public $authenticationManager = null;
/** @var CallbackManager $callbackManager
* @deprecated see getCallbackManager()
*/
public $callbackManager = null;
/** @var Chat $chat
* @deprecated see getChat()
*/
public $chat = null;
/** @var \SimpleXMLElement $config
* @deprecated see getConfig()
*/
public $config = null;
/** @var Configurator $configurator
* @deprecated see getConfigurator()
*/
public $configurator = null;
/** @var Connection $client
* @deprecated see getClient()
*/
public $client = null;
/** @var CommandManager $commandManager
* @deprecated see getCommandManager()
*/
public $commandManager = null;
/** @var Database $database
* @deprecated see getDatabase()
*/
public $database = null;
/** @var ManialinkManager $manialinkManager
* @deprecated see getManialinkManager
*/
public $manialinkManager = null;
/** @var MapManager $mapManager
* @deprecated see getMapManager()
*/
public $mapManager = null;
/** @var PlayerManager $playerManager
* @deprecated see getPlayerManager()
*/
public $playerManager = null;
/** @var PluginManager $pluginManager
* @deprecated see getPluginManager()
*/
public $pluginManager = null;
/** @var Server $server
* @deprecated see getServer()
*/
public $server = null;
/** @var SettingManager $settingManager
* @deprecated see getSettingManager()
*/
public $settingManager = null;
/** @var StatisticManager $statisticManager
* @deprecated see getStatisticManager()
*/
public $statisticManager = null;
/** @var UpdateManager $updateManager
* @deprecated see getUpdateManager()
*/
public $updateManager = null;
/** @var ErrorHandler $errorHandler
* @deprecated see getErrorHandler()
*/
public $errorHandler = null;
/** @var TimerManager $timerManager
* @deprecated see getTimerManager()
*/
public $timerManager = null;
/** @var AsynchronousFileReader $fileReader
* @deprecated see getFileReader()
*/
public $fileReader = null;
/** @var BillManager $billManager
* @deprecated see getBillManager()
*/
public $billManager = null;
/*
* Private properties
*/
private $requestQuitMessage = null;
/**
* Construct a new ManiaControl instance
*/
public function __construct() {
Logger::log('Loading ManiaControl v' . self::VERSION . ' ...');
$this->errorHandler = new ErrorHandler($this);
$this->loadConfig();
// Load ManiaControl Modules
$this->callbackManager = new CallbackManager($this);
$this->timerManager = new TimerManager($this);
$this->database = new Database($this);
$this->fileReader = new AsynchronousFileReader($this);
$this->billManager = new BillManager($this);
$this->settingManager = new SettingManager($this);
$this->statisticManager = new StatisticManager($this);
$this->manialinkManager = new ManialinkManager($this);
$this->actionsMenu = new ActionsMenu($this);
$this->chat = new Chat($this);
$this->commandManager = new CommandManager($this);
$this->server = new Server($this);
$this->authenticationManager = new AuthenticationManager($this);
$this->playerManager = new PlayerManager($this);
$this->mapManager = new MapManager($this);
$this->configurator = new Configurator($this);
$this->pluginManager = new PluginManager($this);
$this->updateManager = new UpdateManager($this);
$this->getErrorHandler()->init();
// Permissions
$this->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_SHUTDOWN, AuthenticationManager::AUTH_LEVEL_SUPERADMIN);
$this->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_RESTART, AuthenticationManager::AUTH_LEVEL_SUPERADMIN);
// Commands
$this->getCommandManager()->registerCommandListener('version', $this, 'commandVersion', false, 'Shows ManiaControl version.');
$this->getCommandManager()->registerCommandListener('restart', $this, 'commandRestart', true, 'Restarts ManiaControl.');
$this->getCommandManager()->registerCommandListener('shutdown', $this, 'commandShutdown', true, 'Shuts ManiaControl down.');
// Check connection every 30 seconds
$this->getTimerManager()->registerTimerListening($this, 'checkConnection', 1000 * 30);
}
/**
* Load the Config XML-File
*/
private function loadConfig() {
$configId = CommandLineHelper::getParameter('-config');
$configFileName = ($configId ? $configId : 'server.xml');
$config = FileUtil::loadConfig($configFileName);
if (!$config) {
$this->quit("Error loading Configuration XML-File! ('{$configFileName}')", true);
}
if ($config->count() < 3) {
$this->quit("Your Configuration File ('{$configFileName}') doesn't seem to be maintained properly. Please check it again!", true);
}
$this->config = $config;
}
/**
* Quit ManiaControl and log the given message
*
* @param string $message
* @param bool $errorPrefix
*/
public function quit($message = null, $errorPrefix = false) {
if ($this->getClient()) {
if ($this->getCallbackManager()) {
// OnShutdown callback
$this->getCallbackManager()->triggerCallback(Callbacks::ONSHUTDOWN);
}
if ($chat = $this->getChat()) {
// Announce quit
try {
$chat->sendInformation('ManiaControl shutting down.');
} catch (TransportException $e) {
}
}
// Hide UI
try {
$this->getClient()->sendHideManialinkPage();
} catch (TransportException $e) {
}
// Delete client
Connection::delete($this->getClient());
$this->client = null;
}
SystemUtil::quit($message, $errorPrefix);
}
/**
* Return the client
*
* @return Connection
*/
public function getClient() {
return $this->client;
}
/**
* Return the callback manager
*
* @return CallbackManager
*/
public function getCallbackManager() {
return $this->callbackManager;
}
/**
* Return the chat
*
* @return Chat
*/
public function getChat() {
return $this->chat;
}
/**
* Return the error handler
*
* @return ErrorHandler
*/
public function getErrorHandler() {
return $this->errorHandler;
}
/**
* Return the authentication manager
*
* @return AuthenticationManager
*/
public function getAuthenticationManager() {
return $this->authenticationManager;
}
/**
* Return the command manager
*
* @return CommandManager
*/
public function getCommandManager() {
return $this->commandManager;
}
/**
* Return the timer manager
*
* @return TimerManager
*/
public function getTimerManager() {
return $this->timerManager;
}
/**
* Print a message to console and log
*
* @param string $message
* @param bool $stripCodes
* @deprecated
* @see Logger::log()
*/
public function log($message, $stripCodes = false) {
Logger::log($message, $stripCodes);
}
/**
* Return the actions menu
*
* @return ActionsMenu
*/
public function getActionsMenu() {
return $this->actionsMenu;
}
/**
* Return the config
*
* @return \SimpleXMLElement
*/
public function getConfig() {
return $this->config;
}
/**
* Return the configurator
*
* @return Configurator
*/
public function getConfigurator() {
return $this->configurator;
}
/**
* Return the database
*
* @return Database
*/
public function getDatabase() {
return $this->database;
}
/**
* Return the manialink manager
*
* @return ManialinkManager
*/
public function getManialinkManager() {
return $this->manialinkManager;
}
/**
* Return the map manager
*
* @return MapManager
*/
public function getMapManager() {
return $this->mapManager;
}
/**
* Return the player manager
*
* @return PlayerManager
*/
public function getPlayerManager() {
return $this->playerManager;
}
/**
* Return the setting manager
*
* @return SettingManager
*/
public function getSettingManager() {
return $this->settingManager;
}
/**
* Return the statistic manager
*
* @return StatisticManager
*/
public function getStatisticManager() {
return $this->statisticManager;
}
/**
* Return the bill manager
*
* @return BillManager
*/
public function getBillManager() {
return $this->billManager;
}
/**
* Check connection
*/
public function checkConnection() {
if ($this->getClient()->getIdleTime() > 180) {
$this->getClient()->getServerName();
}
}
/**
* Handle Version Command
*
* @param array $chatCallback
* @param Player $player
*/
public function commandVersion(array $chatCallback, Player $player) {
$message = 'This server is using ManiaControl v' . ManiaControl::VERSION . '!';
$this->getChat()->sendInformation($message, $player);
}
/**
* Handle Restart AdminCommand
*
* @param array $chatCallback
* @param Player $player
*/
public function commandRestart(array $chatCallback, Player $player) {
if (!$this->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_RESTART)) {
$this->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$this->restart("ManiaControl Restart requested by '{$player->login}'!");
}
/**
* Restart ManiaControl
*
* @param string $message
*/
public function restart($message = null) {
// Announce restart
try {
$this->getChat()->sendInformation('Restarting ManiaControl...');
} catch (TransportException $e) {
}
Logger::log('Restarting ManiaControl... ' . $message);
// Start new instance
SystemUtil::restart();
// Quit old instance
$this->quit('Quitting ManiaControl to restart.');
}
/**
* Handle Shutdown Command
*
* @param array $chat
* @param Player $player
*/
public function commandShutdown(array $chat, Player $player) {
if (!$this->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_SHUTDOWN)) {
$this->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$this->requestQuit("ManiaControl Shutdown requested by '{$player->login}'!");
}
/**
* Request ManiaControl to quit
*
* @param mixed $message
*/
public function requestQuit($message = true) {
$this->requestQuitMessage = $message;
}
/**
* Run ManiaControl
*/
public function run() {
Logger::log('Starting ManiaControl v' . self::VERSION . '!');
try {
// Connect to server
$this->connect();
// Check if the version of the server is high enough
$version = $this->getClient()->getVersion();
if ($version->build < self::MIN_DEDIVERSION) {
$this->quit("The Server has Version '{$version->build}', while at least '" . self::MIN_DEDIVERSION . "' is required!", true);
}
// Listen for shutdown
$this->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_SERVERSTOP, $this, 'handleServerStopCallback');
// OnInit callback
$this->getCallbackManager()->triggerCallback(Callbacks::ONINIT);
// Load plugins
$this->getPluginManager()->loadPlugins();
$this->getUpdateManager()->getPluginUpdateManager()->checkPluginsUpdate();
// AfterInit callback
$this->getCallbackManager()->triggerCallback(Callbacks::AFTERINIT);
// Loading finished
Logger::log('Loading completed!');
Logger::log('Link: ' . $this->getServer()->getJoinLink());
$this->getChat()->sendInformation('ManiaControl v' . self::VERSION . ' successfully started!');
// Main loop
while (!$this->requestQuitMessage) {
$this->loop();
}
// Shutdown
$this->quit($this->requestQuitMessage);
} catch (TransportException $exception) {
Logger::logError('Connection interrupted!');
$this->getErrorHandler()->handleException($exception);
SystemUtil::quit($exception->getMessage(), true);
}
}
/**
* Connect to ManiaPlanet server
*/
private function connect() {
// Load remote client
$serverConfig = $this->getServer()->loadConfig();
Logger::log("Connecting to Server at {$serverConfig->host}:{$serverConfig->port}...");
try {
$this->client = Connection::factory($serverConfig->host, $serverConfig->port, self::SCRIPT_TIMEOUT, $serverConfig->user, $serverConfig->pass, self::API_VERSION);
} catch (TransportException $exception) {
$message = "Couldn't connect to the server: '{$exception->getMessage()}'";
$this->quit($message, true);
} catch (AuthenticationException $exception) {
$message = "Couldn't authenticate on Server with User '{$serverConfig->user}' & Pass '{$serverConfig->pass}'! " . $exception->getMessage();
$this->quit($message, true);
}
// Enable callback system
$this->getClient()->enableCallbacks(true);
// Wait for server to be ready
if (!$this->getServer()->waitForStatus(4)) {
$this->quit("Server couldn't get ready!");
}
// Connect finished
Logger::log('Server Connection successfully established!');
// Hide old widgets
$this->getClient()->sendHideManialinkPage();
// Enable script callbacks
$this->getServer()->getScriptManager()->enableScriptCallbacks();
}
/**
* Return the server
*
* @return Server
*/
public function getServer() {
return $this->server;
}
/**
* Return the plugin manager
*
* @return PluginManager
*/
public function getPluginManager() {
return $this->pluginManager;
}
/**
* Return the update manager
*
* @return UpdateManager
*/
public function getUpdateManager() {
return $this->updateManager;
}
/**
* Perform the Main Loop
*/
private function loop() {
$loopStart = microtime(true);
// Extend script timeout
set_time_limit(self::SCRIPT_TIMEOUT);
// Manage callbacks
$this->getCallbackManager()->manageCallbacks();
// Manage async file reader
$this->getFileReader()->appendData();
// Yield for next tick
$loopEnd = microtime(true);
$loopDuration = $loopEnd - $loopStart;
$sleepTime = (int)(2500 - $loopDuration * 1000000);
if ($sleepTime > 0) {
usleep($sleepTime);
}
}
/**
* Return the file reader
*
* @return AsynchronousFileReader
*/
public function getFileReader() {
return $this->fileReader;
}
/**
* Handle Server Stop Callback
*/
public function handleServerStopCallback() {
$this->requestQuit('The Server has been shut down!');
}
}

View File

@ -0,0 +1,117 @@
<?php
namespace ManiaControl\ManiaExchange;
use ManiaControl\Utils\Formatter;
/**
* Mania Exchange Map Info Object
*
* @author Xymph
* @updated kremsy <kremsy@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class MXMapInfo {
/*
* Public properties
*/
public $prefix, $id, $uid, $name, $userid, $author, $uploaded, $updated, $type, $maptype;
public $titlepack, $style, $envir, $mood, $dispcost, $lightmap, $modname, $exever;
public $exebld, $routes, $length, $unlimiter, $laps, $difficulty, $lbrating, $trkvalue;
public $replaytyp, $replayid, $replaycnt, $authorComment, $commentCount, $awards;
public $pageurl, $replayurl, $imageurl, $thumburl, $downloadurl, $dir;
public $ratingVoteCount, $ratingVoteAverage, $vehicleName;
/**
* Returns map object with all available data from MX map data
*
* @param String $prefix MX URL prefix
* @param $mx
* @return MXMapInfo
*/
public function __construct($prefix, $mx) {
$this->prefix = $prefix;
if (!$mx) {
return;
}
if ($this->prefix === 'tm') {
$this->dir = 'tracks';
$this->id = $mx->TrackID;
$this->uid = isset($mx->TrackUID) ? $mx->TrackUID : '';
} else {
$this->dir = 'maps';
$this->id = $mx->MapID;
$this->uid = isset($mx->MapUID) ? $mx->MapUID : '';
}
if (!isset($mx->GbxMapName) || $mx->GbxMapName === '?') {
$this->name = $mx->Name;
} else {
$this->name = Formatter::stripDirtyCodes($mx->GbxMapName);
}
$this->userid = $mx->UserID;
$this->author = $mx->Username;
$this->uploaded = $mx->UploadedAt;
$this->updated = $mx->UpdatedAt;
$this->type = $mx->TypeName;
$this->maptype = isset($mx->MapType) ? $mx->MapType : '';
$this->titlepack = isset($mx->TitlePack) ? $mx->TitlePack : '';
$this->style = isset($mx->StyleName) ? $mx->StyleName : '';
$this->envir = $mx->EnvironmentName;
$this->mood = $mx->Mood;
$this->dispcost = $mx->DisplayCost;
$this->lightmap = $mx->Lightmap;
$this->modname = isset($mx->ModName) ? $mx->ModName : '';
$this->exever = $mx->ExeVersion;
$this->exebld = $mx->ExeBuild;
$this->routes = isset($mx->RouteName) ? $mx->RouteName : '';
$this->length = isset($mx->LengthName) ? $mx->LengthName : '';
$this->unlimiter = isset($mx->UnlimiterRequired) ? $mx->UnlimiterRequired : false;
$this->laps = isset($mx->Laps) ? $mx->Laps : 0;
$this->difficulty = $mx->DifficultyName;
$this->lbrating = isset($mx->LBRating) ? $mx->LBRating : 0;
$this->trkvalue = isset($mx->TrackValue) ? $mx->TrackValue : 0;
$this->replaytyp = isset($mx->ReplayTypeName) ? $mx->ReplayTypeName : '';
$this->replayid = isset($mx->ReplayWRID) ? $mx->ReplayWRID : 0;
$this->replaycnt = isset($mx->ReplayCount) ? $mx->ReplayCount : 0;
$this->awards = isset($mx->AwardCount) ? $mx->AwardCount : 0;
$this->vehicleName = isset($mx->VehicleName) ? $mx->VehicleName : '';
$this->authorComment = $mx->Comments;
$this->commentCount = $mx->CommentCount;
$this->ratingVoteCount = isset($mx->RatingVoteCount) ? $mx->RatingVoteCount : 0;
$this->ratingVoteAverage = isset($mx->RatingVoteAverage) ? $mx->RatingVoteAverage : 0;
if (!$this->trkvalue && $this->lbrating > 0) {
$this->trkvalue = $this->lbrating;
} elseif (!$this->lbrating && $this->trkvalue > 0) {
$this->lbrating = $this->trkvalue;
}
$this->pageurl = 'http://' . $this->prefix . '.mania-exchange.com/' . $this->dir . '/view/' . $this->id;
$this->downloadurl = 'http://' . $this->prefix . '.mania-exchange.com/' . $this->dir . '/download/' . $this->id;
if ($mx->HasScreenshot) {
$this->imageurl = 'http://' . $this->prefix . '.mania-exchange.com/' . $this->dir . '/screenshot/normal/' . $this->id;
} else {
$this->imageurl = '';
}
if ($mx->HasThumbnail) {
$this->thumburl = 'http://' . $this->prefix . '.mania-exchange.com/' . $this->dir . '/screenshot/small/' . $this->id;
} else {
$this->thumburl = '';
}
if ($this->prefix === 'tm' && $this->replayid > 0) {
$this->replayurl = 'http://' . $this->prefix . '.mania-exchange.com/replays/download/' . $this->replayid;
} else {
$this->replayurl = '';
}
}
}

View File

@ -0,0 +1,356 @@
<?php
namespace ManiaControl\ManiaExchange;
use FML\Controls\Entry;
use FML\Controls\Frame;
use FML\Controls\Gauge;
use FML\Controls\Label;
use FML\Controls\Labels\Label_Button;
use FML\Controls\Labels\Label_Text;
use FML\Controls\Quad;
use FML\Controls\Quads\Quad_BgsPlayerCard;
use FML\Controls\Quads\Quad_Icons64x64_1;
use FML\ManiaLink;
use FML\Script\Features\Paging;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\CallbackManager;
use ManiaControl\ManiaControl;
use ManiaControl\Manialinks\IconManager;
use ManiaControl\Manialinks\ManialinkManager;
use ManiaControl\Manialinks\ManialinkPageAnswerListener;
use ManiaControl\Maps\MapCommands;
use ManiaControl\Maps\MapManager;
use ManiaControl\Players\Player;
use ManiaControl\Utils\ColorUtil;
use ManiaControl\Utils\Formatter;
/**
* ManiaExchange List Widget Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class ManiaExchangeList implements CallbackListener, ManialinkPageAnswerListener {
/*
* Constants
*/
const ACTION_ADD_MAP = 'ManiaExchangeList.AddMap';
const ACTION_SEARCH_MAPNAME = 'ManiaExchangeList.SearchMapName';
const ACTION_SEARCH_AUTHOR = 'ManiaExchangeList.SearchAuthor';
const ACTION_GET_MAPS_FROM_AUTHOR = 'ManiaExchangeList.GetMapsFromAuthor';
const MAX_MX_MAPS_PER_PAGE = 14;
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
private $mapListShown = array();
/**
* Construct a new MX List instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(ManialinkManager::CB_MAIN_WINDOW_CLOSED, $this, 'closeWidget');
$this->maniaControl->getCallbackManager()->registerCallbackListener(ManialinkManager::CB_MAIN_WINDOW_OPENED, $this, 'handleWidgetOpened');
$this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_PLAYERMANIALINKPAGEANSWER, $this, 'handleManialinkPageAnswer');
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ACTION_SEARCH_MAPNAME, $this, 'showList');
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ACTION_SEARCH_AUTHOR, $this, 'showList');
}
/**
* Handle ManialinkPageAnswer Callback
*
* @param array $callback
*/
public function handleManialinkPageAnswer(array $callback) {
$actionId = $callback[1][2];
$actionArray = explode('.', $actionId);
if (count($actionArray) <= 2) {
return;
}
$action = $actionArray[0] . '.' . $actionArray[1];
$login = $callback[1][1];
$player = $this->maniaControl->getPlayerManager()->getPlayer($login);
$mapId = (int)$actionArray[2];
switch ($action) {
case self::ACTION_GET_MAPS_FROM_AUTHOR:
$callback[1][2] = 'auth:' . $actionArray[2];
$this->showList($callback, $player);
break;
case self::ACTION_ADD_MAP:
$this->maniaControl->getMapManager()->addMapFromMx($mapId, $player->login);
break;
}
}
/**
* Shows the List
*
* @param array $chatCallback
* @param Player $player
*/
public function showList(array $chatCallback, Player $player) {
$this->mapListShown[$player->login] = true;
$params = explode(' ', $chatCallback[1][2]);
$searchString = '';
$author = '';
$environment = '';
if (count($params) >= 1) {
foreach ($params as $param) {
if ($param === '/xlist' || $param === MapCommands::ACTION_OPEN_XLIST) {
continue;
}
if ($param === self::ACTION_SEARCH_MAPNAME) {
$searchString = $chatCallback[1][3][0]['Value'];
} else if ($param === self::ACTION_SEARCH_AUTHOR) {
$author = $chatCallback[1][3][0]['Value'];
} else if (strtolower(substr($param, 0, 5)) === 'auth:') {
$author = substr($param, 5);
} else if (strtolower(substr($param, 0, 4)) === 'env:') {
$environment = substr($param, 4);
} else {
if (!$searchString) {
$searchString = $param;
} else {
// concatenate words in name
$searchString .= '%20' . $param;
}
}
}
}
// search for matching maps
$this->maniaControl->getMapManager()->getMXManager()->fetchMapsAsync(function (array $maps) use (&$player) {
if (!$maps) {
$this->maniaControl->getChat()->sendError('No maps found, or MX is down!', $player->login);
return;
}
$this->showManiaExchangeList($maps, $player);
}, $searchString, $author, $environment);
}
/**
* Display the Mania Exchange List
*
* @param MXMapInfo[] $maps
* @param Player $player
* @internal param array $chatCallback
*/
private function showManiaExchangeList(array $maps, Player $player) {
// Start offsets
$width = $this->maniaControl->getManialinkManager()->getStyleManager()->getListWidgetsWidth();
$height = $this->maniaControl->getManialinkManager()->getStyleManager()->getListWidgetsHeight();
$posX = -$width / 2;
$posY = $height / 2;
//Create ManiaLink
$maniaLink = new ManiaLink(ManialinkManager::MAIN_MLID);
$script = $maniaLink->getScript();
$paging = new Paging();
$script->addFeature($paging);
// Main frame
$frame = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultListFrame($script, $paging);
$maniaLink->add($frame);
//Predefine description Label
$descriptionLabel = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultDescriptionLabel();
$frame->add($descriptionLabel);
// Headline
$headFrame = new Frame();
$frame->add($headFrame);
$headFrame->setY($posY - 12);
$array = array('$oId' => $posX + 3.5, '$oName' => $posX + 12.5, '$oAuthor' => $posX + 59, '$oKarma' => $posX + 85, '$oType' => $posX + 103, '$oMood' => $posX + 118, '$oLast Update' => $posX + 130);
$this->maniaControl->getManialinkManager()->labelLine($headFrame, $array);
$index = 0;
$posY = $height / 2 - 16;
$pageFrame = null;
foreach ($maps as $map) {
//TODO order possibilities
if ($index % self::MAX_MX_MAPS_PER_PAGE === 0) {
$pageFrame = new Frame();
$frame->add($pageFrame);
$posY = $height / 2 - 16;
$paging->addPage($pageFrame);
}
// Map Frame
$mapFrame = new Frame();
$pageFrame->add($mapFrame);
if ($index % 2 === 0) {
$lineQuad = new Quad_BgsPlayerCard();
$mapFrame->add($lineQuad);
$lineQuad->setSize($width, 4);
$lineQuad->setSubStyle($lineQuad::SUBSTYLE_BgPlayerCardBig);
$lineQuad->setZ(0.001);
}
$time = Formatter::time_elapsed_string(strtotime($map->updated));
$array = array('$s' . $map->id => $posX + 3.5, '$s' . $map->name => $posX + 12.5, '$s' . $map->author => $posX + 59, '$s' . str_replace('Arena', '', $map->maptype) => $posX + 103, '$s' . $map->mood => $posX + 118, '$s' . $time => $posX + 130);
$labels = $this->maniaControl->getManialinkManager()->labelLine($mapFrame, $array);
$authorLabel = $labels[2];
$authorLabel->setAction(self::ACTION_GET_MAPS_FROM_AUTHOR . '.' . $map->author);
$mapFrame->setY($posY);
$mxQuad = new Quad();
$mapFrame->add($mxQuad);
$mxQuad->setSize(3, 3);
$mxQuad->setImage($this->maniaControl->getManialinkManager()->getIconManager()->getIcon(IconManager::MX_ICON));
$mxQuad->setImageFocus($this->maniaControl->getManialinkManager()->getIconManager()->getIcon(IconManager::MX_ICON_MOVER));
$mxQuad->setX($posX + 56);
$mxQuad->setUrl($map->pageurl);
$mxQuad->setZ(0.01);
$description = 'View $<' . $map->name . '$> on Mania-Exchange';
$mxQuad->addTooltipLabelFeature($descriptionLabel, $description);
if ($this->maniaControl->getAuthenticationManager()->checkPermission($player, MapManager::SETTING_PERMISSION_ADD_MAP)
) {
$addQuad = new Quad_Icons64x64_1();
$mapFrame->add($addQuad);
$addQuad->setX($posX + 53);
$addQuad->setZ(-0.1);
$addQuad->setSubStyle($addQuad::SUBSTYLE_Add);
$addQuad->setSize(4, 4);
$addQuad->setAction(self::ACTION_ADD_MAP . '.' . $map->id);
$addQuad->setZ(0.01);
$description = 'Add-Map: $<' . $map->name . '$>';
$addQuad->addTooltipLabelFeature($descriptionLabel, $description);
}
//Award Quad
if ($map->awards > 0) {
$awardQuad = new Quad_Icons64x64_1();
$mapFrame->add($awardQuad);
$awardQuad->setSize(3, 3);
$awardQuad->setSubStyle($awardQuad::SUBSTYLE_OfficialRace);
$awardQuad->setX($posX + 97);
$awardQuad->setZ(0.01);
$awardLabel = new Label_Text();
$mapFrame->add($awardLabel);
$awardLabel->setX($posX + 98.5);
$awardLabel->setHAlign($awardLabel::LEFT);
$awardLabel->setText($map->awards);
$awardLabel->setTextSize(1.3);
}
//Map Karma
$karma = $map->ratingVoteAverage / 100;
$voteCount = $map->ratingVoteCount;
if (is_numeric($karma) && $voteCount > 0) {
$karmaGauge = new Gauge();
$mapFrame->add($karmaGauge);
$karmaGauge->setZ(2);
$karmaGauge->setX($posX + 89);
$karmaGauge->setSize(16.5, 9);
$karmaGauge->setDrawBg(false);
$karma = floatval($karma);
$karmaGauge->setRatio($karma + 0.15 - $karma * 0.15);
$karmaColor = ColorUtil::floatToStatusColor($karma);
$karmaGauge->setColor($karmaColor . '9');
$karmaLabel = new Label();
$mapFrame->add($karmaLabel);
$karmaLabel->setZ(2);
$karmaLabel->setX($posX + 89);
$karmaLabel->setSize(16.5 * 0.9, 5);
$karmaLabel->setTextSize(0.9);
$karmaLabel->setTextColor('000');
$karmaLabel->setText(' ' . round($karma * 100.) . '% (' . $voteCount . ')');
}
$posY -= 4;
$index++;
}
$label = new Label_Text();
$frame->add($label);
$label->setPosition(-$width / 2 + 5, $height / 2 - 5);
$label->setHAlign($label::LEFT);
$label->setTextSize(1.3);
$label->setText('Search: ');
$entry = new Entry();
$frame->add($entry);
$entry->setStyle(Label_Text::STYLE_TextValueSmall);
$entry->setHAlign($entry::LEFT);
$entry->setPosition(-$width / 2 + 15, $height / 2 - 5);
$entry->setTextSize(1);
$entry->setSize($width * 0.25, 4);
$entry->setName('SearchString');
//Search for Map-Name
$label = new Label_Button();
$frame->add($label);
$label->setPosition(-$width / 2 + 63, $height / 2 - 5);
$label->setText('MapName');
$label->setTextSize(1.3);
$quad = new Quad_BgsPlayerCard();
$frame->add($quad);
$quad->setPosition(-$width / 2 + 63, $height / 2 - 5, 0.01);
$quad->setSubStyle($quad::SUBSTYLE_BgPlayerCardBig);
$quad->setSize(18, 5);
$quad->setAction(self::ACTION_SEARCH_MAPNAME);
//Search for Author
$label = new Label_Button();
$frame->add($label);
$label->setPosition(-$width / 2 + 82, $height / 2 - 5);
$label->setText('Author');
$label->setTextSize(1.3);
$quad = new Quad_BgsPlayerCard();
$frame->add($quad);
$quad->setPosition(-$width / 2 + 82, $height / 2 - 5, 0.01);
$quad->setSubStyle($quad::SUBSTYLE_BgPlayerCardBig);
$quad->setSize(18, 5);
$quad->setAction(self::ACTION_SEARCH_AUTHOR);
// render and display xml
$this->maniaControl->getManialinkManager()->displayWidget($maniaLink, $player, 'ManiaExchangeList');
}
/**
* Unset the player if he opened another Main Widget
*
* @param Player $player
* @param $openedWidget
*/
public function handleWidgetOpened(Player $player, $openedWidget) {
//unset when another main widget got opened
if ($openedWidget !== 'ManiaExchangeList') {
unset($this->mapListShown[$player->login]);
}
}
/**
* Closes the widget
*
* @param \ManiaControl\Players\Player $player
* @internal param array $callback
*/
public function closeWidget(Player $player) {
unset($this->mapListShown[$player->login]);
}
}

View File

@ -0,0 +1,375 @@
<?php
namespace ManiaControl\ManiaExchange;
use ManiaControl\Files\AsynchronousFileReader;
use ManiaControl\ManiaControl;
use ManiaControl\Maps\Map;
use ManiaControl\Maps\MapManager;
use Maniaplanet\DedicatedServer\Xmlrpc\GameModeException;
/**
* Mania Exchange Info Searcher Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class ManiaExchangeManager {
/*
* Constants
*/
//Search others
const SEARCH_ORDER_NONE = -1;
const SEARCH_ORDER_TRACK_NAME = 0;
const SEARCH_ORDER_AUTHOR = 1;
const SEARCH_ORDER_UPLOADED_NEWEST = 2;
const SEARCH_ORDER_UPLOADED_OLDEST = 3;
const SEARCH_ORDER_UPDATED_NEWEST = 4;
const SEARCH_ORDER_UPDATED_OLDEST = 5;
const SEARCH_ORDER_ACTIVITY_LATEST = 6;
const SEARCH_ORDER_ACTIVITY_OLDEST = 7;
const SEARCH_ORDER_AWARDS_MOST = 8;
const SEARCH_ORDER_AWARDS_LEAST = 9;
const SEARCH_ORDER_COMMENTS_MOST = 10;
const SEARCH_ORDER_COMMENTS_LEAST = 11;
const SEARCH_ORDER_DIFFICULTY_EASIEST = 12;
const SEARCH_ORDER_DIFFICULTY_HARDEST = 13;
const SEARCH_ORDER_LENGTH_SHORTEST = 14;
const SEARCH_ORDER_LENGTH_LONGEST = 15;
//Maximum Maps per request
const MAPS_PER_MX_FETCH = 50;
const MIN_EXE_BUILD = "2014-04-01_00_00";
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
private $mxIdUidVector = array();
/**
* Construct map manager
*
* @param \ManiaControl\ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
}
/**
* Unset Map by Mx Id
*
* @param int $mxId
*/
public function unsetMap($mxId) {
if (isset($this->mxIdUidVector[$mxId])) {
unset($this->mxIdUidVector[$mxId]);
}
}
/**
* Fetch Map Information from Mania Exchange
*
* @param mixed $maps
*/
public function fetchManiaExchangeMapInformation($maps = null) {
if ($maps) {
// Fetch Information for a single map
$maps = array($maps);
} else {
// Fetch Information for whole MapList
$maps = $this->maniaControl->getMapManager()->getMaps();
}
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$mapIdString = '';
// Fetch mx ids
$fetchMapQuery = "SELECT `mxid`, `changed` FROM `" . MapManager::TABLE_MAPS . "`
WHERE `index` = ?;";
$fetchMapStatement = $mysqli->prepare($fetchMapQuery);
if ($mysqli->error) {
trigger_error($mysqli->error);
return;
}
$index = 0;
foreach ($maps as $map) {
if (!$map) {
// TODO: remove after resolving of error report about "non-object"
$this->maniaControl->getErrorHandler()->triggerDebugNotice('Non-Object-Map', $map, $maps);
continue;
}
/** @var Map $map */
$fetchMapStatement->bind_param('i', $map->index);
$fetchMapStatement->execute();
if ($fetchMapStatement->error) {
trigger_error($fetchMapStatement->error);
continue;
}
$fetchMapStatement->store_result();
$fetchMapStatement->bind_result($mxId, $changed);
$fetchMapStatement->fetch();
$fetchMapStatement->free_result();
//Set changed time into the map object
$map->lastUpdate = strtotime($changed);
if ($mxId) {
$appendString = $mxId . ',';
//Set the mx id to the mxidmapvektor
$this->mxIdUidVector[$mxId] = $map->uid;
} else {
$appendString = $map->uid . ',';
}
$index++;
//If Max Maplimit is reached, or string gets too long send the request
if ($index % self::MAPS_PER_MX_FETCH === 0) {
$mapIdString = substr($mapIdString, 0, -1);
$this->fetchMaplistByMixedUidIdString($mapIdString);
$mapIdString = '';
}
$mapIdString .= $appendString;
}
if ($mapIdString) {
$mapIdString = substr($mapIdString, 0, -1);
$this->fetchMaplistByMixedUidIdString($mapIdString);
}
$fetchMapStatement->close();
}
/**
* Fetch the whole Map List from MX via mixed Uid and Id Strings
*
* @param string $string
*/
public function fetchMaplistByMixedUidIdString($string) {
// Get Title Prefix
$titlePrefix = $this->maniaControl->getMapManager()->getCurrentMap()->getGame();
// compile search URL
$url = "http://api.mania-exchange.com/{$titlePrefix}/maps/?ids={$string}";
$this->maniaControl->getFileReader()->loadFile($url, function ($mapInfo, $error) use ($titlePrefix, $url) {
if ($error) {
trigger_error("Error: '{$error}' for Url '{$url}'");
return;
}
if (!$mapInfo) {
return;
}
$mxMapList = json_decode($mapInfo);
if ($mxMapList === null) {
trigger_error("Can't decode searched JSON Data from Url '{$url}'");
return;
}
$maps = array();
foreach ($mxMapList as $map) {
if ($map) {
$mxMapObject = new MXMapInfo($titlePrefix, $map);
if ($mxMapObject) {
array_push($maps, $mxMapObject);
}
}
}
$this->updateMapObjectsWithManiaExchangeIds($maps);
}, AsynchronousFileReader::CONTENT_TYPE_JSON);
}
/**
* Store MX Map Info in the Database and the MX Info in the Map Object
*
* @param array $mxMapInfos
*/
public function updateMapObjectsWithManiaExchangeIds(array $mxMapInfos) {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
// Save map data
$saveMapQuery = "UPDATE `" . MapManager::TABLE_MAPS . "`
SET `mxid` = ?
WHERE `uid` = ?;";
$saveMapStatement = $mysqli->prepare($saveMapQuery);
if ($mysqli->error) {
trigger_error($mysqli->error);
return;
}
$saveMapStatement->bind_param('is', $mapMxId, $mapUId);
foreach ($mxMapInfos as $mxMapInfo) {
/** @var MXMapInfo $mxMapInfo */
$mapMxId = $mxMapInfo->id;
$mapUId = $mxMapInfo->uid;
$saveMapStatement->execute();
if ($saveMapStatement->error) {
trigger_error($saveMapStatement->error);
}
//Take the uid out of the vector
if (isset($this->mxIdUidVector[$mxMapInfo->id])) {
$uid = $this->mxIdUidVector[$mxMapInfo->id];
} else {
$uid = $mxMapInfo->uid;
}
$map = $this->maniaControl->getMapManager()->getMapByUid($uid);
if ($map) {
// TODO: how does it come that $map can be empty here? we got an error report for that
/** @var Map $map */
$map->mx = $mxMapInfo;
}
}
$saveMapStatement->close();
}
/**
* @deprecated
* @see \ManiaControl\ManiaExchange\ManiaExchangeManager::fetchMaplistByMixedUidIdString()
*/
public function getMaplistByMixedUidIdString($string) {
$this->fetchMaplistByMixedUidIdString($string);
return true;
}
/**
* Fetch Map Info asynchronously
*
* @param int $mapId
* @param callable $function
*/
public function fetchMapInfo($mapId, callable $function) {
// Get Title Prefix
$titlePrefix = $this->maniaControl->getMapManager()->getCurrentMap()->getGame();
// compile search URL
$url = 'http://api.mania-exchange.com/' . $titlePrefix . '/maps/?ids=' . $mapId;
$this->maniaControl->getFileReader()->loadFile($url, function ($mapInfo, $error) use (&$function, $titlePrefix, $url) {
$mxMapInfo = null;
if ($error) {
trigger_error($error);
} else {
$mxMapList = json_decode($mapInfo);
if (!is_array($mxMapList)) {
trigger_error('Cannot decode searched JSON data from ' . $url);
} else if (!empty($mxMapList)) {
$mxMapInfo = new MXMapInfo($titlePrefix, $mxMapList[0]);
}
}
call_user_func($function, $mxMapInfo);
}, AsynchronousFileReader::CONTENT_TYPE_JSON);
}
/**
* @deprecated
* @see \ManiaControl\ManiaExchange\ManiaExchangeManager::fetchMapsAsync()
*/
public function getMapsAsync(callable $function, $name = '', $author = '', $env = '', $maxMapsReturned = 100, $searchOrder = self::SEARCH_ORDER_UPDATED_NEWEST) {
$this->fetchMapsAsync($function, $name, $author, $env, $maxMapsReturned, $searchOrder);
return true;
}
/**
* Fetch a MapList Asynchronously
*
* @param callable $function
* @param string $name
* @param string $author
* @param string $env
* @param int $maxMapsReturned
* @param int $searchOrder
*/
public function fetchMapsAsync(callable $function, $name = '', $author = '', $env = '', $maxMapsReturned = 100, $searchOrder = self::SEARCH_ORDER_UPDATED_NEWEST) {
// TODO: remove $env because it's not really used?
// Get Title Id
$titleId = $this->maniaControl->getServer()->titleId;
$titlePrefix = $this->maniaControl->getMapManager()->getCurrentMap()->getGame();
// compile search URL
$url = 'http://' . $titlePrefix . '.mania-exchange.com/tracksearch2/search?api=on';
$game = explode('@', $titleId);
$envNumber = $this->getEnvironment($game[0]);
if ($env || $envNumber > -1) {
$url .= '&environments=' . $envNumber;
}
if ($name) {
$url .= '&trackname=' . str_replace(" ", "%20", $name);
}
if ($author) {
$url .= '&author=' . $author;
}
$url .= '&priord=' . $searchOrder;
$url .= '&limit=' . $maxMapsReturned;
if ($titlePrefix !== "tm") {
$url .= '&minexebuild=' . self::MIN_EXE_BUILD;
}
// Get MapTypes
try {
$scriptInfos = $this->maniaControl->getClient()->getModeScriptInfo();
$mapTypes = $scriptInfos->compatibleMapTypes;
$url .= '&mtype=' . $mapTypes;
} catch (GameModeException $e) {
}
$this->maniaControl->getFileReader()->loadFile($url, function ($mapInfo, $error) use (&$function, $titlePrefix) {
if ($error) {
trigger_error($error);
return;
}
$mxMapList = json_decode($mapInfo);
if (!isset($mxMapList->results)) {
trigger_error('Cannot decode searched JSON data');
return;
}
$mxMapList = $mxMapList->results;
if ($mxMapList === null) {
trigger_error('Cannot decode searched JSON data');
return;
}
$maps = array();
foreach ($mxMapList as $map) {
if (!empty($map)) {
array_push($maps, new MXMapInfo($titlePrefix, $map));
}
}
call_user_func($function, $maps);
}, AsynchronousFileReader::CONTENT_TYPE_JSON);
}
/**
* Get the Current Environment by String
*
* @param string $env
* @return int
*/
private function getEnvironment($env) {
switch ($env) {
case 'TMCanyon':
return 1;
case 'TMStadium':
return 2;
case 'TMValley':
return 3;
default:
return -1;
}
}
}

View File

@ -0,0 +1,173 @@
<?php
namespace ManiaControl\Manialinks;
use FML\CustomUI;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\TimerListener;
use ManiaControl\ManiaControl;
use ManiaControl\Players\Player;
use ManiaControl\Players\PlayerManager;
/**
* Class managing the Custom UI in TrackMania
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class CustomUIManager implements CallbackListener, TimerListener {
/*
* Constants
*/
const CUSTOMUI_MLID = 'CustomUI.MLID';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/** @var customUI $customUI */
private $customUI = null;
private $updateManialink = false;
/**
* Create a custom UI manager instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
$this->prepareManialink();
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(PlayerManager::CB_PLAYERCONNECT, $this, 'handlePlayerJoined');
$this->maniaControl->getTimerManager()->registerTimerListening($this, 'handle1Second', 1000);
}
/**
* Create the ManiaLink and CustomUI instances
*/
private function prepareManialink() {
$this->customUI = new CustomUI();
}
/**
* Handle 1 Second Callback
*/
public function handle1Second() {
if (!$this->updateManialink) {
return;
}
$this->updateManialink = false;
$this->updateManialink();
}
/**
* Update the CustomUI Manialink
*
* @param Player $player
*/
public function updateManialink(Player $player = null) {
if ($player) {
$this->maniaControl->getManialinkManager()->sendManialink($this->customUI, $player);
return;
}
$this->maniaControl->getManialinkManager()->sendManialink($this->customUI);
}
/**
* Handle PlayerJoined Callback
*
* @param Player $player
*/
public function handlePlayerJoined(Player $player) {
$this->updateManialink($player);
//TODO: validate necessity
//send it again after 500ms
$this->maniaControl->getTimerManager()->registerOneTimeListening($this, function () use (&$player) {
$this->updateManialink($player);
}, 500);
}
/**
* Set Showing of Notices
*
* @param bool $visible
*/
public function setNoticeVisible($visible) {
$this->customUI->setNoticeVisible($visible);
$this->updateManialink = true;
}
/**
* Set Showing of the Challenge Info
*
* @param bool $visible
*/
public function setChallengeInfoVisible($visible) {
$this->customUI->setChallengeInfoVisible($visible);
$this->updateManialink = true;
}
/**
* Set Showing of the Net Infos
*
* @param bool $visible
*/
public function setNetInfosVisible($visible) {
$this->customUI->setNetInfosVisible($visible);
$this->updateManialink = true;
}
/**
* Set Showing of the Chat
*
* @param bool $visible
*/
public function setChatVisible($visible) {
$this->customUI->setChatVisible($visible);
$this->updateManialink = true;
}
/**
* Set Showing of the Checkpoint List
*
* @param bool $visible
*/
public function setCheckpointListVisible($visible) {
$this->customUI->setCheckpointListVisible($visible);
$this->updateManialink = true;
}
/**
* Set Showing of Round Scores
*
* @param bool $visible
*/
public function setRoundScoresVisible($visible) {
$this->customUI->setRoundScoresVisible($visible);
$this->updateManialink = true;
}
/**
* Set Showing of the Scoretable
*
* @param bool $visible
*/
public function setScoretableVisible($visible) {
$this->customUI->setScoretableVisible($visible);
$this->updateManialink = true;
}
/**
* Set Global Showing
*
* @param bool $visible
*/
public function setGlobalVisible($visible) {
$this->customUI->setGlobalVisible($visible);
$this->updateManialink = true;
}
}

View File

@ -0,0 +1,128 @@
<?php
namespace ManiaControl\Manialinks;
use FML\Controls\Frame;
use FML\Controls\Quad;
use FML\ManiaLink;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\Callbacks;
use ManiaControl\ManiaControl;
use ManiaControl\Players\Player;
use ManiaControl\Players\PlayerManager;
/**
* Class managing Icons
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class IconManager implements CallbackListener {
/*
* Constants
*/
const DEFAULT_IMG_URL = 'http://images.maniacontrol.com/icons/';
const PRELOAD_MLID = 'IconManager.Preload.MLID';
/*
* Default icons
*/
const MX_ICON = 'ManiaExchange.png';
const MX_ICON_MOVER = 'ManiaExchange_logo_press.png';
const MX_ICON_GREEN = 'ManiaExchangeGreen.png';
const MX_ICON_GREEN_MOVER = 'ManiaExchange_logo_pressGreen.png';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
private $icons = array();
/**
* Construct a new icon manager instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
$this->addDefaultIcons();
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::AFTERINIT, $this, 'handleAfterInit');
$this->maniaControl->getCallbackManager()->registerCallbackListener(PlayerManager::CB_PLAYERCONNECT, $this, 'handlePlayerConnect');
}
/**
* Add the set of default icons
*/
private function addDefaultIcons() {
$this->addIcon(self::MX_ICON);
$this->addIcon(self::MX_ICON_MOVER);
$this->addIcon(self::MX_ICON_GREEN);
$this->addIcon(self::MX_ICON_GREEN_MOVER);
}
/**
* Add an Icon
*
* @param string $iconName
* @param string $iconLink
*/
public function addIcon($iconName, $iconLink = self::DEFAULT_IMG_URL) {
$this->icons[$iconName] = $iconLink . '/' . $iconName;
}
/**
* Get an Icon by its Name
*
* @param string $iconName
* @return string
*/
public function getIcon($iconName) {
if (!isset($this->icons[$iconName])) {
return null;
}
return $this->icons[$iconName];
}
/**
* Handle OnInit Callback
*/
public function handleAfterInit() {
$this->preloadIcons();
}
/**
* Preload Icons
*
* @param Player $player
*/
public function preloadIcons($player = null) {
$maniaLink = new ManiaLink(self::PRELOAD_MLID);
$frame = new Frame();
$maniaLink->add($frame);
$frame->setPosition(500, 500);
foreach ($this->icons as $iconUrl) {
$iconQuad = new Quad();
$iconQuad->setImage($iconUrl);
$iconQuad->setSize(1, 1);
$frame->add($iconQuad);
}
// Send manialink
$this->maniaControl->getManialinkManager()->sendManialink($maniaLink, $player);
}
/**
* Handle PlayerConnect Callback
*
* @param Player $player
*/
public function handlePlayerConnect(Player $player) {
$this->preloadIcons($player);
}
}

View File

@ -0,0 +1,377 @@
<?php
namespace ManiaControl\Manialinks;
use FML\Controls\Control;
use FML\Controls\Frame;
use FML\Controls\Labels\Label_Text;
use FML\ManiaLink;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\CallbackManager;
use ManiaControl\ManiaControl;
use ManiaControl\Players\Player;
use Maniaplanet\DedicatedServer\Xmlrpc\GameModeException;
use Maniaplanet\DedicatedServer\Xmlrpc\UnknownPlayerException;
/**
* Manialink Manager Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class ManialinkManager implements ManialinkPageAnswerListener, CallbackListener {
/*
* Constants
*/
const MAIN_MLID = 'Main.ManiaLinkId';
const ACTION_CLOSEWIDGET = 'ManiaLinkManager.CloseWidget';
const CB_MAIN_WINDOW_CLOSED = 'ManialinkManagerCallback.MainWindowClosed';
const CB_MAIN_WINDOW_OPENED = 'ManialinkManagerCallback.MainWindowOpened';
/*
* Public properties
*/
/** @var StyleManager $styleManager */
/** @deprecated see getStyleManager() */
public $styleManager = null;
/** @var CustomUIManager $customUIManager */
/** @deprecated see getCustomUIManager() */
public $customUIManager = null;
/** @var IconManager $iconManager */
/** @deprecated see getIconManager() */
public $iconManager = null;
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
// TODO: use listening class
private $pageAnswerListeners = array();
private $pageAnswerRegexListener = array();
/**
* Construct a new manialink manager instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Children
$this->styleManager = new StyleManager($maniaControl);
$this->customUIManager = new CustomUIManager($maniaControl);
$this->iconManager = new IconManager($maniaControl);
// Callbacks
$this->registerManialinkPageAnswerListener(self::ACTION_CLOSEWIDGET, $this, 'closeWidgetCallback');
$this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_PLAYERMANIALINKPAGEANSWER, $this, 'handleManialinkPageAnswer');
}
/**
* Register a new manialink page answer listener
*
* @param string $actionId
* @param ManialinkPageAnswerListener $listener
* @param string $method
* @return bool
*/
public function registerManialinkPageAnswerListener($actionId, ManialinkPageAnswerListener $listener, $method) {
if (!method_exists($listener, $method)) {
trigger_error("Given listener for actionId '{$actionId}' doesn't have callback method '{$method}'!");
return false;
}
if (!array_key_exists($actionId, $this->pageAnswerListeners) || !is_array($this->pageAnswerListeners[$actionId])) {
// Init listeners array
$this->pageAnswerListeners[$actionId] = array();
}
// Register page answer listener
array_push($this->pageAnswerListeners[$actionId], array($listener, $method));
return true;
}
/**
* Return the style manager
*
* @return StyleManager
*/
public function getStyleManager() {
return $this->styleManager;
}
/**
* Return the custom UI manager
*
* @return CustomUIManager
*/
public function getCustomUIManager() {
return $this->customUIManager;
}
/**
* Return the icon manager
*
* @return IconManager
*/
public function getIconManager() {
return $this->iconManager;
}
/**
* Register a new manialink page answer reg ex listener
*
* @param string $actionIdRegex
* @param ManialinkPageAnswerListener $listener
* @param string $method
* @return bool
*/
public function registerManialinkPageAnswerRegexListener($actionIdRegex, ManialinkPageAnswerListener $listener, $method) {
if (!method_exists($listener, $method)) {
trigger_error("Given listener for actionIdRegex '{$actionIdRegex}' doesn't have callback method '{$method}'!");
return false;
}
if (!array_key_exists($actionIdRegex, $this->pageAnswerRegexListener) || !is_array($this->pageAnswerRegexListener[$actionIdRegex])) {
// Init regex listeners array
$this->pageAnswerRegexListener[$actionIdRegex] = array();
}
// Register page answer regex listener
array_push($this->pageAnswerRegexListener[$actionIdRegex], array($listener, $method));
return true;
}
/**
* Remove a Manialink Page Answer Listener
*
* @param ManialinkPageAnswerListener $listener
* @return bool
*/
public function unregisterManialinkPageAnswerListener(ManialinkPageAnswerListener $listener) {
$removed = false;
$allListeners = array_merge($this->pageAnswerListeners, $this->pageAnswerRegexListener);
foreach ($allListeners as &$listeners) {
foreach ($listeners as $key => &$listenerCallback) {
if ($listenerCallback[0] !== $listener) {
continue;
}
unset($listeners[$key]);
$removed = true;
}
}
return $removed;
}
/**
* Handle ManialinkPageAnswer callback
*
* @param array $callback
*/
public function handleManialinkPageAnswer(array $callback) {
$actionId = $callback[1][2];
$login = $callback[1][1];
$player = $this->maniaControl->getPlayerManager()->getPlayer($login);
if (array_key_exists($actionId, $this->pageAnswerListeners) && is_array($this->pageAnswerListeners[$actionId])) {
// Inform page answer listeners
foreach ($this->pageAnswerListeners[$actionId] as $listener) {
call_user_func($listener, $callback, $player);
}
}
// Check regex listeners
foreach ($this->pageAnswerRegexListener as $actionIdRegex => $pageAnswerRegexListeners) {
if (preg_match($actionIdRegex, $actionId)) {
// Inform page answer regex listeners
foreach ($pageAnswerRegexListeners as $listener) {
call_user_func($listener, $callback, $player);
}
}
}
}
/**
* Displays a ManiaLink Widget to a certain Player (Should only be used on Main Widgets)
*
* @param mixed $maniaLink
* @param mixed $player
* @param string $widgetName
*/
public function displayWidget($maniaLink, $player, $widgetName = null) {
// render and display xml
$this->sendManialink($maniaLink, $player);
if ($widgetName) {
// TODO make check by manialinkId, getter is needed to avoid uses on non main widgets
$this->disableAltMenu($player);
// Trigger callback
$player = $this->maniaControl->getPlayerManager()->getPlayer($player);
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_MAIN_WINDOW_OPENED, $player, $widgetName);
}
}
/**
* Send the given manialink to players
*
* @param string $manialinkText
* @param mixed $logins
* @param int $timeout
* @param bool $hideOnClick
* @return bool
*/
public function sendManialink($manialinkText, $logins = null, $timeout = 0, $hideOnClick = false) {
$manialinkText = (string)$manialinkText;
if (!$manialinkText) {
return true;
}
try {
if (!$logins) {
return $this->maniaControl->getClient()->sendDisplayManialinkPage(null, $manialinkText, $timeout, $hideOnClick);
}
if (is_string($logins)) {
$success = $this->maniaControl->getClient()->sendDisplayManialinkPage($logins, $manialinkText, $timeout, $hideOnClick);
return $success;
}
if ($logins instanceof Player) {
$success = $this->maniaControl->getClient()->sendDisplayManialinkPage($logins->login, $manialinkText, $timeout, $hideOnClick);
return $success;
}
if (is_array($logins)) {
$success = true;
foreach ($logins as $login) {
$subSuccess = $this->sendManialink($manialinkText, $login, $timeout, $hideOnClick);
if (!$subSuccess) {
$success = false;
}
}
return $success;
}
} catch (UnknownPlayerException $e) {
return false;
}
return true;
}
/**
* Disable the alt menu for the player
*
* @param mixed $player
* @return bool
*/
public function disableAltMenu($player) {
$login = Player::parseLogin($player);
try {
$success = $this->maniaControl->getClient()->triggerModeScriptEvent('LibXmlRpc_DisableAltMenu', $login);
} catch (GameModeException $e) {
return false;
}
return $success;
}
/**
* Closes a widget via the callback
*
* @param array $callback
* @param Player $player
*/
public function closeWidgetCallback(array $callback, Player $player) {
$this->closeWidget($player);
}
/**
* Closes a Manialink Widget
*
* @param mixed $player
* @param bool $widgetId
*/
public function closeWidget($player, $widgetId = false) {
if (!$widgetId) {
$this->hideManialink(self::MAIN_MLID, $player);
$this->enableAltMenu($player);
// Trigger callback
$player = $this->maniaControl->getPlayerManager()->getPlayer($player);
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_MAIN_WINDOW_CLOSED, $player);
} else {
$this->hideManialink($widgetId, $player);
}
}
/**
* Hide the Manialink with the given Id
*
* @param mixed $manialinkId
* @param mixed $logins
*/
public function hideManialink($manialinkId, $logins = null) {
if (is_array($manialinkId)) {
foreach ($manialinkId as $mlId) {
$this->hideManialink($mlId, $logins);
}
} else {
$emptyManialink = new ManiaLink($manialinkId);
$this->sendManialink($emptyManialink, $logins);
}
}
/**
* Enable the alt menu for the player
*
* @param mixed $player
* @return bool
*/
public function enableAltMenu($player) {
$login = Player::parseLogin($player);
try {
$success = $this->maniaControl->getClient()->triggerModeScriptEvent('LibXmlRpc_EnableAltMenu', $login);
} catch (GameModeException $e) {
return false;
}
return $success;
}
/**
* Adds a line of labels
*
* @param Frame $frame
* @param array $labelStrings
* @param array $properties
* @return Label_Text[]
*/
public function labelLine(Frame $frame, array $labelStrings, array $properties = array()) {
// define standard properties
$hAlign = (isset($properties['hAlign']) ? $properties['hAlign'] : Control::LEFT);
$style = (isset($properties['style']) ? $properties['style'] : Label_Text::STYLE_TextCardSmall);
$textSize = (isset($properties['textSize']) ? $properties['textSize'] : 1.5);
$textColor = (isset($properties['textColor']) ? $properties['textColor'] : 'FFF');
$profile = (isset($properties['profile']) ? $properties['profile'] : false);
$labels = array();
foreach ($labelStrings as $text => $x) {
$label = new Label_Text();
$frame->add($label);
$label->setHAlign($hAlign);
$label->setX($x);
$label->setStyle($style);
$label->setTextSize($textSize);
$label->setText($text);
$label->setTextColor($textColor);
if ($profile) {
$label->addPlayerProfileFeature($profile);
}
array_push($labels, $label);
}
return $labels;
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace ManiaControl\Manialinks;
/**
* Interface for Manialink Page Answer Listeners
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
interface ManialinkPageAnswerListener {
}

View File

@ -0,0 +1,213 @@
<?php
namespace ManiaControl\Manialinks;
use FML\Controls\Frame;
use FML\Controls\Label;
use FML\Controls\Labels\Label_Text;
use FML\Controls\Quad;
use FML\Controls\Quads\Quad_BgRaceScore2;
use FML\Controls\Quads\Quad_Bgs1InRace;
use FML\Controls\Quads\Quad_Icons64x64_1;
use FML\Script\Features\Paging;
use FML\Script\Script;
use ManiaControl\ManiaControl;
/**
* Class managing default Control Styles
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class StyleManager {
/*
* Constants
*/
const SETTING_LABEL_DEFAULT_STYLE = 'Default Label Style';
const SETTING_QUAD_DEFAULT_STYLE = 'Default Quad Style';
const SETTING_QUAD_DEFAULT_SUBSTYLE = 'Default Quad SubStyle';
const SETTING_MAIN_WIDGET_DEFAULT_STYLE = 'Main Widget Default Quad Style';
const SETTING_MAIN_WIDGET_DEFAULT_SUBSTYLE = 'Main Widget Default Quad SubStyle';
const SETTING_LIST_WIDGETS_WIDTH = 'List Widgets Width';
const SETTING_LIST_WIDGETS_HEIGHT = 'List Widgets Height';
const SETTING_ICON_DEFAULT_OFFSET_SM = 'Default Icon Offset in ShootMania';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/**
* Construct a new style manager instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Settings
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_LABEL_DEFAULT_STYLE, Label_Text::STYLE_TextTitle1);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_QUAD_DEFAULT_STYLE, Quad_Bgs1InRace::STYLE);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_QUAD_DEFAULT_SUBSTYLE, Quad_Bgs1InRace::SUBSTYLE_BgTitleShadow);
// Main Widget
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_MAIN_WIDGET_DEFAULT_STYLE, Quad_BgRaceScore2::STYLE);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_MAIN_WIDGET_DEFAULT_SUBSTYLE, Quad_BgRaceScore2::SUBSTYLE_HandleSelectable);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_LIST_WIDGETS_WIDTH, 150.);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_LIST_WIDGETS_HEIGHT, 80.);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_ICON_DEFAULT_OFFSET_SM, 20.);
}
/**
* Get the default Icon Offset for shootmania
*
* @return float
*/
public function getDefaultIconOffsetSM() {
return $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_ICON_DEFAULT_OFFSET_SM);
}
/**
* Get the default label style
*
* @return string
*/
public function getDefaultLabelStyle() {
return $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_LABEL_DEFAULT_STYLE);
}
/**
* Get the default quad style
*
* @return string
*/
public function getDefaultQuadStyle() {
return $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_QUAD_DEFAULT_STYLE);
}
/**
* Get the default quad substyle
*
* @return string
*/
public function getDefaultQuadSubstyle() {
return $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_QUAD_DEFAULT_SUBSTYLE);
}
/**
* Gets the Default Description Label
*
* @return \FML\Controls\Label
*/
public function getDefaultDescriptionLabel() {
$width = $this->getListWidgetsWidth();
$height = $this->getListWidgetsHeight();
// Predefine Description Label
$descriptionLabel = new Label();
$descriptionLabel->setAlign($descriptionLabel::LEFT, $descriptionLabel::TOP)->setPosition($width * -0.5 + 10, $height * -0.5 + 5)->setSize($width * 0.7, 4)->setTextSize(2)->setVisible(false);
return $descriptionLabel;
}
/**
* Get the Default List Widgets Width
*
* @return float
*/
public function getListWidgetsWidth() {
return $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_LIST_WIDGETS_WIDTH);
}
/**
* Get the default list widget height
*
* @return float
*/
public function getListWidgetsHeight() {
return $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_LIST_WIDGETS_HEIGHT);
}
/**
* Builds the Default List Frame
*
* @param mixed $script
* @param mixed $paging
* @return \FML\Controls\Frame
*/
public function getDefaultListFrame($script = null, $paging = null) {
$args = func_get_args();
$script = null;
$paging = null;
foreach ($args as $arg) {
if ($arg instanceof Script) {
$script = $arg;
}
if ($arg instanceof Paging) {
$paging = $arg;
}
}
$width = $this->getListWidgetsWidth();
$height = $this->getListWidgetsHeight();
$quadStyle = $this->getDefaultMainWindowStyle();
$quadSubstyle = $this->getDefaultMainWindowSubStyle();
// mainframe
$frame = new Frame();
$frame->setSize($width, $height)->setZ(35); //TODO place before scoreboards
// Background Quad
$backgroundQuad = new Quad();
$frame->add($backgroundQuad);
$backgroundQuad->setZ(-2)->setSize($width, $height)->setStyles($quadStyle, $quadSubstyle);
// Add Close Quad (X)
$closeQuad = new Quad_Icons64x64_1();
$frame->add($closeQuad);
$closeQuad->setPosition($width * 0.483, $height * 0.467, 3)->setSize(6, 6)->setSubStyle($closeQuad::SUBSTYLE_QuitRace)->setAction(ManialinkManager::ACTION_CLOSEWIDGET);
if ($script) {
$pagerSize = 6.;
$pagerPrev = new Quad_Icons64x64_1();
$frame->add($pagerPrev);
$pagerPrev->setPosition($width * 0.42, $height * -0.44, 2)->setSize($pagerSize, $pagerSize)->setSubStyle($pagerPrev::SUBSTYLE_ArrowPrev);
$pagerNext = new Quad_Icons64x64_1();
$frame->add($pagerNext);
$pagerNext->setPosition($width * 0.45, $height * -0.44, 2)->setSize($pagerSize, $pagerSize)->setSubStyle($pagerNext::SUBSTYLE_ArrowNext);
$pageCountLabel = new Label_Text();
$frame->add($pageCountLabel);
$pageCountLabel->setHAlign($pageCountLabel::RIGHT)->setPosition($width * 0.40, $height * -0.44, 1)->setStyle($pageCountLabel::STYLE_TextTitle1)->setTextSize(1.3);
if ($paging) {
$paging->addButton($pagerNext)->addButton($pagerPrev)->setLabel($pageCountLabel);
}
}
return $frame;
}
/**
* Get the default main window style
*
* @return string
*/
public function getDefaultMainWindowStyle() {
return $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_MAIN_WIDGET_DEFAULT_STYLE);
}
/**
* Get the default main window substyle
*
* @return string
*/
public function getDefaultMainWindowSubStyle() {
return $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_MAIN_WIDGET_DEFAULT_SUBSTYLE);
}
}

View File

@ -0,0 +1,392 @@
<?php
namespace ManiaControl\Maps;
use FML\Controls\Frame;
use FML\Controls\Label;
use FML\Controls\Labels\Label_Text;
use FML\Controls\Quads\Quad_BgsPlayerCard;
use FML\Controls\Quads\Quad_Icons64x64_1;
use FML\Controls\Quads\Quad_UIConstruction_Buttons;
use FML\Controls\Quads\Quad_UIConstructionBullet_Buttons;
use FML\ManiaLink;
use FML\Script\Features\Paging;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
use ManiaControl\Manialinks\ManialinkManager;
use ManiaControl\Manialinks\ManialinkPageAnswerListener;
use ManiaControl\Players\Player;
use Maniaplanet\DedicatedServer\Xmlrpc\AlreadyInListException;
use Maniaplanet\DedicatedServer\Xmlrpc\FileException;
use Maniaplanet\DedicatedServer\Xmlrpc\InvalidMapException;
/**
* Maps Directory Browser
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class DirectoryBrowser implements ManialinkPageAnswerListener {
/*
* Constants
*/
const ACTION_SHOW = 'MapsDirBrowser.Show';
const ACTION_NAVIGATE_UP = 'MapsDirBrowser.NavigateUp';
const ACTION_NAVIGATE_ROOT = 'MapsDirBrowser.NavigateRoot';
const ACTION_OPEN_FOLDER = 'MapsDirBrowser.OpenFolder.';
const ACTION_INSPECT_FILE = 'MapsDirBrowser.InspectFile.';
const ACTION_ADD_FILE = 'MapsDirBrowser.AddFile.';
const ACTION_ERASE_FILE = 'MapsDirBrowser.EraseFile.';
const WIDGET_NAME = 'MapsDirBrowser.Widget';
const CACHE_FOLDER_PATH = 'FolderPath';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/**
* Construct a new directory browser instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// ManiaLink Actions
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ACTION_SHOW, $this, 'handleActionShow');
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ACTION_NAVIGATE_UP, $this, 'handleNavigateUp');
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ACTION_NAVIGATE_ROOT, $this, 'handleNavigateRoot');
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerRegexListener($this->buildActionRegex(self::ACTION_OPEN_FOLDER), $this, 'handleOpenFolder');
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerRegexListener($this->buildActionRegex(self::ACTION_INSPECT_FILE), $this, 'handleInspectFile');
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerRegexListener($this->buildActionRegex(self::ACTION_ADD_FILE), $this, 'handleAddFile');
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerRegexListener($this->buildActionRegex(self::ACTION_ERASE_FILE), $this, 'handleEraseFile');
}
/**
* Build the regex to register for the given action
*
* @param string $actionName
* @return string
*/
private function buildActionRegex($actionName) {
return '/' . $actionName . '*/';
}
/**
* Handle 'Show' action
*
* @param array $actionCallback
* @param Player $player
*/
public function handleActionShow(array $actionCallback, Player $player) {
$this->showManiaLink($player);
}
/**
* Build and show the Browser ManiaLink to the given Player
*
* @param Player $player
* @param mixed $nextFolder
*/
public function showManiaLink(Player $player, $nextFolder = null) {
$oldFolderPath = $player->getCache($this, self::CACHE_FOLDER_PATH);
$isInMapsFolder = false;
if (!$oldFolderPath) {
$oldFolderPath = $this->maniaControl->getServer()->getDirectory()->getMapsFolder();
$isInMapsFolder = true;
}
$folderPath = $oldFolderPath;
if (is_string($nextFolder)) {
$newFolderPath = realpath($oldFolderPath . $nextFolder);
if ($newFolderPath) {
$folderPath = $newFolderPath . DIRECTORY_SEPARATOR;
$folderName = basename($newFolderPath);
switch ($folderName) {
case 'Maps':
$mapsDir = dirname($this->maniaControl->getServer()->getDirectory()->getMapsFolder());
$folderDir = dirname($folderPath);
$isInMapsFolder = ($mapsDir === $folderDir);
break;
case 'UserData':
$dataDir = dirname($this->maniaControl->getServer()->getDirectory()->getGameDataFolder());
$folderDir = dirname($folderPath);
if ($dataDir === $folderDir) {
// Prevent navigation out of maps directory
return;
}
break;
}
}
}
$player->setCache($this, self::CACHE_FOLDER_PATH, $folderPath);
$maniaLink = new ManiaLink(ManialinkManager::MAIN_MLID);
$script = $maniaLink->getScript();
$paging = new Paging();
$script->addFeature($paging);
$frame = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultListFrame($script, $paging);
$maniaLink->add($frame);
$width = $this->maniaControl->getManialinkManager()->getStyleManager()->getListWidgetsWidth();
$height = $this->maniaControl->getManialinkManager()->getStyleManager()->getListWidgetsHeight();
$index = 0;
$posY = $height / 2 - 10;
$pageFrame = null;
$navigateRootQuad = new Quad_Icons64x64_1();
$frame->add($navigateRootQuad);
$navigateRootQuad->setPosition($width * -0.47, $height * 0.45)->setSize(4, 4)->setSubStyle($navigateRootQuad::SUBSTYLE_ToolRoot);
$navigateUpQuad = new Quad_Icons64x64_1();
$frame->add($navigateUpQuad);
$navigateUpQuad->setPosition($width * -0.44, $height * 0.45)->setSize(4, 4)->setSubStyle($navigateUpQuad::SUBSTYLE_ToolUp);
if (!$isInMapsFolder) {
$navigateRootQuad->setAction(self::ACTION_NAVIGATE_ROOT);
$navigateUpQuad->setAction(self::ACTION_NAVIGATE_UP);
}
$directoryLabel = new Label_Text();
$frame->add($directoryLabel);
$dataFolder = $this->maniaControl->getServer()->getDirectory()->getGameDataFolder();
$directoryText = substr($folderPath, strlen($dataFolder));
$directoryLabel->setPosition($width * -0.41, $height * 0.45)->setSize($width * 0.85, 4)->setHAlign($directoryLabel::LEFT)->setText($directoryText)->setTextSize(2);
$tooltipLabel = new Label();
$frame->add($tooltipLabel);
$tooltipLabel->setPosition($width * -0.48, $height * -0.44)->setSize($width * 0.8, 5)->setHAlign($tooltipLabel::LEFT)->setTextSize(1)->setText('tooltip');
$mapFiles = $this->scanMapFiles($folderPath);
if (is_array($mapFiles)) {
if (empty($mapFiles)) {
$emptyLabel = new Label();
$frame->add($emptyLabel);
$emptyLabel->setY(20)->setTextColor('aaa')->setText('No files found.')->setTranslate(true);
} else {
$canAddMaps = $this->maniaControl->getAuthenticationManager()->checkPermission($player, MapManager::SETTING_PERMISSION_ADD_MAP);
$canEraseMaps = $this->maniaControl->getAuthenticationManager()->checkPermission($player, MapManager::SETTING_PERMISSION_ERASE_MAP);
foreach ($mapFiles as $filePath => $fileName) {
$shortFilePath = substr($filePath, strlen($folderPath));
if ($index % 15 === 0) {
// New Page
$pageFrame = new Frame();
$frame->add($pageFrame);
$posY = $height / 2 - 10;
$paging->addPage($pageFrame);
}
// Map Frame
$mapFrame = new Frame();
$pageFrame->add($mapFrame);
$mapFrame->setY($posY);
if ($index % 2 === 0) {
// Striped background line
$lineQuad = new Quad_BgsPlayerCard();
$mapFrame->add($lineQuad);
$lineQuad->setZ(-1)->setSize($width, 4)->setSubStyle($lineQuad::SUBSTYLE_BgPlayerCardBig);
}
// File name Label
$nameLabel = new Label_Text();
$mapFrame->add($nameLabel);
$nameLabel->setX($width * -0.48)->setSize($width * 0.79, 4)->setHAlign($nameLabel::LEFT)->setStyle($nameLabel::STYLE_TextCardRaceRank)->setTextSize(1)->setText($fileName);
if (is_dir($filePath)) {
// Folder
$nameLabel->setAction(self::ACTION_OPEN_FOLDER . substr($shortFilePath, 0, -1))->addTooltipLabelFeature($tooltipLabel, 'Open folder ' . $fileName);
} else {
// File
$nameLabel->setAction(self::ACTION_INSPECT_FILE . $fileName)->addTooltipLabelFeature($tooltipLabel, 'Inspect file ' . $fileName);
if ($canAddMaps) {
// 'Add' button
$addButton = new Quad_UIConstructionBullet_Buttons();
$mapFrame->add($addButton);
$addButton->setX($width * 0.42)->setSize(4, 4)->setSubStyle($addButton::SUBSTYLE_NewBullet)->setAction(self::ACTION_ADD_FILE . $fileName)->addTooltipLabelFeature($tooltipLabel, 'Add map ' . $fileName);
}
if ($canEraseMaps) {
// 'Erase' button
$eraseButton = new Quad_UIConstruction_Buttons();
$mapFrame->add($eraseButton);
$eraseButton->setX($width * 0.46)->setSize(4, 4)->setSubStyle($eraseButton::SUBSTYLE_Erase)->setAction(self::ACTION_ERASE_FILE . $fileName)->addTooltipLabelFeature($tooltipLabel, 'Erase file ' . $fileName);
}
}
$posY -= 4;
$index++;
}
}
} else {
$errorLabel = new Label();
$frame->add($errorLabel);
$errorLabel->setY(20)->setTextColor('f30')->setText('No access to the directory.')->setTranslate(true);
}
$this->maniaControl->getManialinkManager()->displayWidget($maniaLink, $player, self::WIDGET_NAME);
}
/**
* Scan the given directory for Map files
*
* @param string $directory
* @return array|bool
*/
protected function scanMapFiles($directory) {
if (!is_readable($directory) || !is_dir($directory)) {
return false;
}
$mapFiles = array();
$dirFiles = scandir($directory);
foreach ($dirFiles as $fileName) {
if (substr($fileName, 0, 1) === '.') {
continue;
}
$fullFileName = $directory . $fileName;
if (!is_readable($fullFileName)) {
continue;
}
if (is_dir($fullFileName)) {
$mapFiles[$fullFileName . DIRECTORY_SEPARATOR] = $fileName . DIRECTORY_SEPARATOR;
continue;
} else {
if ($this->isMapFileName($fileName)) {
$mapFiles[$fullFileName] = $fileName;
}
}
}
return $mapFiles;
}
/**
* Check if the given file name represents a Map file
*
* @param string $fileName
* @return bool
*/
protected function isMapFileName($fileName) {
$mapFileNameEnding = '.map.gbx';
$fileNameEnding = strtolower(substr($fileName, -strlen($mapFileNameEnding)));
return ($fileNameEnding === $mapFileNameEnding);
}
/**
* Handle 'NavigateRoot' action
*
* @param array $actionCallback
* @param Player $player
*/
public function handleNavigateRoot(array $actionCallback, Player $player) {
$player->destroyCache($this, self::CACHE_FOLDER_PATH);
$this->showManiaLink($player);
}
/**
* Handle 'NavigateUp' action
*
* @param array $actionCallback
* @param Player $player
*/
public function handleNavigateUp(array $actionCallback, Player $player) {
$this->showManiaLink($player, '..');
}
/**
* Handle 'OpenFolder' page action
*
* @param array $actionCallback
* @param Player $player
*/
public function handleOpenFolder(array $actionCallback, Player $player) {
$actionName = $actionCallback[1][2];
$folderName = substr($actionName, strlen(self::ACTION_OPEN_FOLDER));
$this->showManiaLink($player, $folderName);
}
/**
* Handle 'InspectFile' page action
*
* @param array $actionCallback
* @param Player $player
*/
public function handleInspectFile(array $actionCallback, Player $player) {
$actionName = $actionCallback[1][2];
$fileName = substr($actionName, strlen(self::ACTION_INSPECT_FILE));
// TODO: show inspect file view
var_dump($fileName);
}
/**
* Handle 'AddFile' page action
*
* @param array $actionCallback
* @param Player $player
*/
public function handleAddFile(array $actionCallback, Player $player) {
$actionName = $actionCallback[1][2];
$fileName = substr($actionName, strlen(self::ACTION_ADD_FILE));
$folderPath = $player->getCache($this, self::CACHE_FOLDER_PATH);
$filePath = $folderPath . $fileName;
$mapsFolder = $this->maniaControl->getServer()->getDirectory()->getMapsFolder();
$relativeFilePath = substr($filePath, strlen($mapsFolder));
// Check for valid map
try {
$this->maniaControl->getClient()->checkMapForCurrentServerParams($relativeFilePath);
} catch (InvalidMapException $exception) {
$this->maniaControl->getChat()->sendException($exception, $player);
return;
} catch (FileException $exception) {
$this->maniaControl->getChat()->sendException($exception, $player);
return;
}
// Add map to map list
try {
$this->maniaControl->getClient()->insertMap($relativeFilePath);
} catch (AlreadyInListException $exception) {
$this->maniaControl->getChat()->sendException($exception, $player);
return;
}
$map = $this->maniaControl->getMapManager()->fetchMapByFileName($relativeFilePath);
if (!$map) {
$this->maniaControl->getChat()->sendError('Error occurred.', $player);
return;
}
// Message
$message = $player->getEscapedNickname() . ' added ' . $map->getEscapedName() . '!';
$this->maniaControl->getChat()->sendSuccess($message);
Logger::logInfo($message, true);
// Queue requested Map
$this->maniaControl->getMapManager()->getMapQueue()->addMapToMapQueue($player, $map);
}
/**
* Handle 'EraseFile' page action
*
* @param array $actionCallback
* @param Player $player
*/
public function handleEraseFile(array $actionCallback, Player $player) {
$actionName = $actionCallback[1][2];
$fileName = substr($actionName, strlen(self::ACTION_ERASE_FILE));
$folderPath = $player->getCache($this, self::CACHE_FOLDER_PATH);
$filePath = $folderPath . $fileName;
if (@unlink($filePath)) {
$this->maniaControl->getChat()->sendSuccess("Erased {$fileName}!");
$this->showManiaLink($player);
} else {
$this->maniaControl->getChat()->sendError("Couldn't erase {$fileName}!");
}
}
}

113
core/Maps/Map.php Normal file
View File

@ -0,0 +1,113 @@
<?php
namespace ManiaControl\Maps;
use ManiaControl\ManiaExchange\MXMapInfo;
use ManiaControl\Utils\Formatter;
/**
* ManiaControl Map Model Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class Map {
/*
* Public properties
*/
public $index = -1;
public $name = 'undefined';
public $rawName = null;
public $uid = null;
public $fileName = null;
public $environment = null;
public $authorTime = -1;
public $goldTime = -1;
public $copperPrice = -1;
public $mapType = null;
public $mapStyle = null;
public $nbCheckpoints = -1;
public $nbLaps = -1;
/** @var MXMapInfo $mx */
public $mx = null;
public $authorLogin = null;
public $authorNick = null;
public $authorZone = null;
public $authorEInfo = null;
public $comment = null;
public $titleUid = null;
public $startTime = -1;
public $lastUpdate = 0;
public $karma = null;
/**
* Construct a new map instance from xmlrpc data
*
* @param \Maniaplanet\DedicatedServer\Structures\Map $mpMap
*/
public function __construct($mpMap = null) {
$this->startTime = time();
if (!$mpMap) {
return;
}
$this->name = Formatter::stripDirtyCodes($mpMap->name);
$this->rawName = $mpMap->name;
$this->uid = $mpMap->uId;
$this->fileName = $mpMap->fileName;
$this->authorLogin = $mpMap->author;
$this->environment = $mpMap->environnement;
$this->authorTime = $mpMap->authorTime;
$this->goldTime = $mpMap->goldTime;
$this->copperPrice = $mpMap->copperPrice;
$this->mapType = $mpMap->mapType;
$this->mapStyle = $mpMap->mapStyle;
$this->nbCheckpoints = $mpMap->nbCheckpoints;
$this->nbLaps = $mpMap->nbLaps;
$this->authorNick = $this->authorLogin;
}
/**
* Get the escaped map name
*
* @return string
*/
public function getEscapedName() {
return Formatter::escapeText($this->name);
}
/**
* Get the game type of the map
*
* @return string
*/
public function getGame() {
switch ($this->environment) {
case 'Storm':
return 'sm';
case 'Canyon':
case 'Stadium':
case 'Valley':
return 'tm';
}
return null;
}
/**
* Check whether a map update is available
*
* @return bool
*/
public function updateAvailable() {
return ($this->mx && ($this->lastUpdate < strtotime($this->mx->updated) || $this->uid !== $this->mx->uid));
}
/**
* Var_Dump the Map
*/
public function dump() {
var_dump(json_decode(json_encode($this)));
}
}

47
core/Maps/MapActions.php Normal file
View File

@ -0,0 +1,47 @@
<?php
namespace ManiaControl\Maps;
use ManiaControl\ManiaControl;
use Maniaplanet\DedicatedServer\Xmlrpc\ChangeInProgressException;
/**
* ManiaControl Map Actions Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class MapActions {
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/**
* Construct a map actions instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
}
/**
* Skip the current Map
*/
public function skipMap() {
// Force an EndMap on the MapQueue to set the next Map
$this->maniaControl->getMapManager()->getMapQueue()->endMap(null);
// Ignore EndMap on MapQueue
$this->maniaControl->getMapManager()->getMapQueue()->dontQueueNextMapChange();
// Switch The Map
try {
$this->maniaControl->getClient()->nextMap();
} catch (ChangeInProgressException $e) {
}
}
}

511
core/Maps/MapCommands.php Normal file
View File

@ -0,0 +1,511 @@
<?php
namespace ManiaControl\Maps;
use FML\Controls\Quad;
use FML\Controls\Quads\Quad_Icons64x64_1;
use FML\Controls\Quads\Quad_UIConstruction_Buttons;
use ManiaControl\Admin\AuthenticationManager;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\CallbackManager;
use ManiaControl\Commands\CommandListener;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
use ManiaControl\Manialinks\IconManager;
use ManiaControl\Manialinks\ManialinkPageAnswerListener;
use ManiaControl\Players\Player;
use Maniaplanet\DedicatedServer\Xmlrpc\ChangeInProgressException;
use Maniaplanet\DedicatedServer\Xmlrpc\FaultException;
/**
* Class offering Commands to manage Maps
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class MapCommands implements CommandListener, ManialinkPageAnswerListener, CallbackListener {
/*
* Constants
*/
const ACTION_OPEN_MAPLIST = 'MapCommands.OpenMapList';
const ACTION_OPEN_XLIST = 'MapCommands.OpenMXList';
const ACTION_RESTART_MAP = 'MapCommands.RestartMap';
const ACTION_SKIP_MAP = 'MapCommands.NextMap';
const ACTION_SHOW_AUTHOR = 'MapList.ShowAuthorList.';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/**
* Construct a new map commands instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
$this->initActionsMenuButtons();
// Admin commands
$this->maniaControl->getCommandManager()->registerCommandListener(array('nextmap', 'next', 'skip'), $this, 'command_NextMap', true, 'Skips to the next map.');
$this->maniaControl->getCommandManager()->registerCommandListener(array('restartmap', 'resmap', 'res'), $this, 'command_RestartMap', true, 'Restarts the current map.');
$this->maniaControl->getCommandManager()->registerCommandListener(array('replaymap', 'replay'), $this, 'command_ReplayMap', true, 'Replays the current map (after the end of the map).');
$this->maniaControl->getCommandManager()->registerCommandListener(array('addmap', 'add'), $this, 'command_AddMap', true, 'Adds map from ManiaExchange.');
$this->maniaControl->getCommandManager()->registerCommandListener(array('removemap', 'removethis'), $this, 'command_RemoveMap', true, 'Removes the current map.');
$this->maniaControl->getCommandManager()->registerCommandListener(array('erasemap', 'erasethis'), $this, 'command_EraseMap', true, 'Erases the current map.');
$this->maniaControl->getCommandManager()->registerCommandListener(array('shufflemaps', 'shuffle'), $this, 'command_ShuffleMaps', true, 'Shuffles the maplist.');
$this->maniaControl->getCommandManager()->registerCommandListener(array('writemaplist', 'wml'), $this, 'command_WriteMapList', true, 'Writes the current maplist to a file.');
$this->maniaControl->getCommandManager()->registerCommandListener(array('readmaplist', 'rml'), $this, 'command_ReadMapList', true, 'Loads a maplist into the server.');
// Player commands
$this->maniaControl->getCommandManager()->registerCommandListener('nextmap', $this, 'command_showNextMap', false, 'Shows which map is next.');
$this->maniaControl->getCommandManager()->registerCommandListener(array('maps', 'list'), $this, 'command_List', false, 'Shows the current maplist (or variations).');
$this->maniaControl->getCommandManager()->registerCommandListener(array('xmaps', 'xlist'), $this, 'command_xList', false, 'Shows maps from ManiaExchange.');
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_PLAYERMANIALINKPAGEANSWER, $this, 'handleManialinkPageAnswer');
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ACTION_OPEN_XLIST, $this, 'command_xList');
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ACTION_OPEN_MAPLIST, $this, 'command_List');
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ACTION_RESTART_MAP, $this, 'command_RestartMap');
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ACTION_SKIP_MAP, $this, 'command_NextMap');
}
/**
* Add all Actions Menu Buttons
*/
private function initActionsMenuButtons() {
// Menu Open xList
$itemQuad = new Quad();
$itemQuad->setImage($this->maniaControl->getManialinkManager()->getIconManager()->getIcon(IconManager::MX_ICON));
$itemQuad->setImageFocus($this->maniaControl->getManialinkManager()->getIconManager()->getIcon(IconManager::MX_ICON_MOVER));
$itemQuad->setAction(self::ACTION_OPEN_XLIST);
$this->maniaControl->getActionsMenu()->addPlayerMenuItem($itemQuad, 5, 'Open MX List');
// Menu Open List
$itemQuad = new Quad_Icons64x64_1();
$itemQuad->setSubStyle($itemQuad::SUBSTYLE_ToolRoot);
$itemQuad->setAction(self::ACTION_OPEN_MAPLIST);
$this->maniaControl->getActionsMenu()->addPlayerMenuItem($itemQuad, 10, 'Open MapList');
// Menu RestartMap
$itemQuad = new Quad_UIConstruction_Buttons();
$itemQuad->setSubStyle($itemQuad::SUBSTYLE_Reload);
$itemQuad->setAction(self::ACTION_RESTART_MAP);
$this->maniaControl->getActionsMenu()->addAdminMenuItem($itemQuad, 10, 'Restart Map');
// Menu NextMap
$itemQuad = new Quad_Icons64x64_1();
$itemQuad->setSubStyle($itemQuad::SUBSTYLE_ArrowFastNext);
$itemQuad->setAction(self::ACTION_SKIP_MAP);
$this->maniaControl->getActionsMenu()->addAdminMenuItem($itemQuad, 20, 'Skip Map');
}
/**
* Show which map is the next
*
* @param array $chatCallback
* @param Player $player
*/
public function command_ShowNextMap(array $chatCallback, Player $player) {
$nextQueued = $this->maniaControl->getMapManager()->getMapQueue()->getNextQueuedMap();
if ($nextQueued) {
/** @var Player $requester */
$requester = $nextQueued[0];
/** @var Map $map */
$map = $nextQueued[1];
$this->maniaControl->getChat()->sendInformation("Next Map is $<{$map->name}$> from $<{$map->authorNick}$> requested by $<{$requester->nickname}$>.", $player);
} else {
$mapIndex = $this->maniaControl->getClient()->getNextMapIndex();
$maps = $this->maniaControl->getMapManager()->getMaps();
$map = $maps[$mapIndex];
$this->maniaControl->getChat()->sendInformation("Next Map is $<{$map->name}$> from $<{$map->authorNick}$>.", $player);
}
}
/**
* Handle //removemap command
*
* @param array $chatCallback
* @param Player $player
*/
public function command_RemoveMap(array $chatCallback, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, MapManager::SETTING_PERMISSION_REMOVE_MAP)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
// Get map
$map = $this->maniaControl->getMapManager()->getCurrentMap();
if (!$map) {
$this->maniaControl->getChat()->sendError("Couldn't remove map.", $player);
return;
}
// Remove map
$this->maniaControl->getMapManager()->removeMap($player, $map->uid);
}
/**
* Handle //erasemap command
*
* @param array $chatCallback
* @param Player $player
*/
public function command_EraseMap(array $chatCallback, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, MapManager::SETTING_PERMISSION_ERASE_MAP)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
// Get map
$map = $this->maniaControl->getMapManager()->getCurrentMap();
if (!$map) {
$this->maniaControl->getChat()->sendError("Couldn't erase map.", $player);
return;
}
// Erase map
$this->maniaControl->getMapManager()->removeMap($player, $map->uid, true);
}
/**
* Handle shufflemaps command
*
* @param array $chatCallback
* @param \ManiaControl\Players\Player $player
*/
public function command_ShuffleMaps(array $chatCallback, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, MapManager::SETTING_PERMISSION_SHUFFLE_MAPS)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
// Shuffles the maps
$this->maniaControl->getMapManager()->shuffleMapList($player);
}
/**
* Handle addmap command
*
* @param array $chatCallback
* @param \ManiaControl\Players\Player $player
*/
public function command_AddMap(array $chatCallback, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, MapManager::SETTING_PERMISSION_ADD_MAP)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$params = explode(' ', $chatCallback[1][2], 2);
if (count($params) < 2) {
$this->maniaControl->getChat()->sendUsageInfo('Usage example: //addmap 1234', $player);
return;
}
// add Map from Mania Exchange
$this->maniaControl->getMapManager()->addMapFromMx($params[1], $player->login);
}
/**
* Handle /nextmap Command
*
* @param array $chat
* @param \ManiaControl\Players\Player $player
*/
public function command_NextMap(array $chat, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, MapManager::SETTING_PERMISSION_SKIP_MAP)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$this->maniaControl->getMapManager()->getMapActions()->skipMap();
$message = $player->getEscapedNickname() . ' skipped the current Map!';
$this->maniaControl->getChat()->sendSuccess($message);
Logger::logInfo($message, true);
}
/**
* Handle restartmap command
*
* @param array $chat
* @param \ManiaControl\Players\Player $player
*/
public function command_RestartMap(array $chat, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, MapManager::SETTING_PERMISSION_RESTART_MAP)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$message = $player->getEscapedNickname() . ' restarted the current Map!';
$this->maniaControl->getChat()->sendSuccess($message);
Logger::logInfo($message, true);
try {
$this->maniaControl->getClient()->restartMap();
} catch (ChangeInProgressException $e) {
}
}
////$this->maniaControl->mapManager->mapQueue->addFirstMapToMapQueue($this->currentVote->voter, $this->maniaControl->mapManager->getCurrentMap());
/**
* Handle replaymap command
*
* @param array $chat
* @param \ManiaControl\Players\Player $player
*/
public function command_ReplayMap(array $chat, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, MapManager::SETTING_PERMISSION_RESTART_MAP)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$message = $player->getEscapedNickname() . ' replays the current Map!';
$this->maniaControl->getChat()->sendSuccess($message);
Logger::logInfo($message, true);
$this->maniaControl->getMapManager()->getMapQueue()->addFirstMapToMapQueue($player, $this->maniaControl->getMapManager()->getCurrentMap());
}
/**
* Handle writemaplist command
*
* @param array $chat
* @param \ManiaControl\Players\Player $player
*/
public function command_WriteMapList(array $chat, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkRight($player, AuthenticationManager::AUTH_LEVEL_SUPERADMIN)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$chatCommand = explode(' ', $chat[1][2]);
if (isset($chatCommand[1])) {
if (strstr($chatCommand[1], '.txt')) {
$maplist = $chatCommand[1];
} else {
$maplist = $chatCommand[1] . '.txt';
}
} else {
$maplist = 'maplist.txt';
}
$maplist = 'MatchSettings' . DIRECTORY_SEPARATOR . $maplist;
try {
$this->maniaControl->getClient()->saveMatchSettings($maplist);
$message = 'Maplist $<$fff' . $maplist . '$> written.';
$this->maniaControl->getChat()->sendSuccess($message, $player);
Logger::logInfo($message, true);
} catch (FaultException $e) {
$this->maniaControl->getChat()->sendError('Cannot write maplist $<$fff' . $maplist . '$>!', $player);
}
}
/**
* Handle readmaplist command
*
* @param array $chat
* @param \ManiaControl\Players\Player $player
*/
public function command_ReadMapList(array $chat, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkRight($player, AuthenticationManager::AUTH_LEVEL_SUPERADMIN)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$chatCommand = explode(' ', $chat[1][2]);
if (isset($chatCommand[1])) {
if (strstr($chatCommand[1], '.txt')) {
$maplist = $chatCommand[1];
} else {
$maplist = $chatCommand[1] . '.txt';
}
} else {
$maplist = 'maplist.txt';
}
$maplist = 'MatchSettings' . DIRECTORY_SEPARATOR . $maplist;
try {
$this->maniaControl->getClient()->loadMatchSettings($maplist);
$message = 'Maplist $<$fff' . $maplist . '$> loaded.';
$this->maniaControl->getMapManager()->restructureMapList();
$this->maniaControl->getChat()->sendSuccess($message, $player);
Logger::logInfo($message, true);
} catch (FaultException $e) {
$this->maniaControl->getChat()->sendError('Cannot load maplist $<$fff' . $maplist . '$>!', $player);
}
}
/**
* Handle ManialinkPageAnswer Callback
*
* @param array $callback
*/
public function handleManialinkPageAnswer(array $callback) {
$actionId = $callback[1][2];
$login = $callback[1][1];
$player = $this->maniaControl->getPlayerManager()->getPlayer($login);
if (strstr($actionId, self::ACTION_SHOW_AUTHOR)) {
$login = str_replace(self::ACTION_SHOW_AUTHOR, '', $actionId);
$this->maniaControl->getMapManager()->getMapList()->playerCloseWidget($player);
$this->showMapListAuthor($login, $player);
}
}
/**
* Show the Player a List of Maps from the given Author
*
* @param string $author
* @param Player $player
*/
private function showMapListAuthor($author, Player $player) {
$maps = $this->maniaControl->getMapManager()->getMaps();
$mapList = array();
/** @var Map $map */
foreach ($maps as $map) {
if ($map->authorLogin == $author) {
array_push($mapList, $map);
}
}
if (empty($mapList)) {
$this->maniaControl->getChat()->sendError('There are no maps to show!', $player->login);
return;
}
$this->maniaControl->getMapManager()->getMapList()->showMapList($player, $mapList);
}
/**
* Handle /maps command
*
* @param array $chatCallback
* @param Player $player
*/
public function command_List(array $chatCallback, Player $player) {
$chatCommands = explode(' ', $chatCallback[1][2]);
$this->maniaControl->getMapManager()->getMapList()->playerCloseWidget($player);
if (isset($chatCommands[1])) {
$listParam = strtolower($chatCommands[1]);
switch ($listParam) {
case 'best':
$this->showMapListKarma(true, $player);
break;
case 'worst':
$this->showMapListKarma(false, $player);
break;
case 'newest':
$this->showMapListDate(true, $player);
break;
case 'oldest':
$this->showMapListDate(false, $player);
break;
case 'author':
if (isset($chatCommands[2])) {
$this->showMaplistAuthor($chatCommands[2], $player);
} else {
$this->maniaControl->getChat()->sendError('Missing Author Login!', $player);
}
break;
default:
$this->maniaControl->getMapManager()->getMapList()->showMapList($player);
break;
}
} else {
$this->maniaControl->getMapManager()->getMapList()->showMapList($player);
}
}
/**
* Show a Karma based MapList
*
* @param bool $best
* @param Player $player
*/
private function showMapListKarma($best, Player $player) {
/** @var \MCTeam\KarmaPlugin $karmaPlugin */
$karmaPlugin = $this->maniaControl->getPluginManager()->getPlugin(MapList::DEFAULT_KARMA_PLUGIN);
if ($karmaPlugin) {
$maps = $this->maniaControl->getMapManager()->getMaps();
$mapList = array();
foreach ($maps as $map) {
if ($map instanceof Map) {
if ($this->maniaControl->getSettingManager()->getSettingValue($karmaPlugin, $karmaPlugin::SETTING_NEWKARMA) === true
) {
$karma = $karmaPlugin->getMapKarma($map);
$map->karma = round($karma * 100.);
} else {
$votes = $karmaPlugin->getMapVotes($map);
$min = 0;
$plus = 0;
foreach ($votes as $vote) {
if (isset($vote->vote)) {
if ($vote->vote !== 0.5) {
if ($vote->vote < 0.5) {
$min = $min + $vote->count;
} else {
$plus = $plus + $vote->count;
}
}
}
}
$map->karma = $plus - $min;
}
$mapList[] = $map;
}
}
usort($mapList, function (Map $mapA, Map $mapB) {
return ($mapA->karma - $mapB->karma);
});
if ($best) {
$mapList = array_reverse($mapList);
}
$this->maniaControl->getMapManager()->getMapList()->showMapList($player, $mapList);
} else {
$this->maniaControl->getChat()->sendError('KarmaPlugin is not enabled!', $player->login);
}
}
/**
* Show a Date based MapList
*
* @param bool $newest
* @param Player $player
*/
private function showMapListDate($newest, Player $player) {
$maps = $this->maniaControl->getMapManager()->getMaps();
usort($maps, function (Map $mapA, Map $mapB) {
return ($mapA->index - $mapB->index);
});
if ($newest) {
$maps = array_reverse($maps);
}
$this->maniaControl->getMapManager()->getMapList()->showMapList($player, $maps);
}
/**
* Handle ManiaExchange list command
*
* @param array $chatCallback
* @param Player $player
*/
public function command_xList(array $chatCallback, Player $player) {
$this->maniaControl->getMapManager()->getMXList()->showList($chatCallback, $player);
}
}

724
core/Maps/MapList.php Normal file
View File

@ -0,0 +1,724 @@
<?php
namespace ManiaControl\Maps;
use FML\Controls\Frame;
use FML\Controls\Gauge;
use FML\Controls\Label;
use FML\Controls\Labels\Label_Button;
use FML\Controls\Labels\Label_Text;
use FML\Controls\Quad;
use FML\Controls\Quads\Quad_BgsPlayerCard;
use FML\Controls\Quads\Quad_Icons64x64_1;
use FML\Controls\Quads\Quad_UIConstruction_Buttons;
use FML\ManiaLink;
use FML\Script\Features\Paging;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\CallbackManager;
use ManiaControl\Callbacks\Callbacks;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
use ManiaControl\Manialinks\IconManager;
use ManiaControl\Manialinks\ManialinkManager;
use ManiaControl\Manialinks\ManialinkPageAnswerListener;
use ManiaControl\Players\Player;
use ManiaControl\Utils\ColorUtil;
use ManiaControl\Utils\Formatter;
use Maniaplanet\DedicatedServer\Xmlrpc\ChangeInProgressException;
use Maniaplanet\DedicatedServer\Xmlrpc\FileException;
use Maniaplanet\DedicatedServer\Xmlrpc\NextMapException;
use Maniaplanet\DedicatedServer\Xmlrpc\NotInListException;
use MCTeam\CustomVotesPlugin;
use MCTeam\KarmaPlugin;
/**
* MapList Widget Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class MapList implements ManialinkPageAnswerListener, CallbackListener {
/*
* Constants
*/
const ACTION_UPDATE_MAP = 'MapList.UpdateMap';
const ACTION_REMOVE_MAP = 'MapList.RemoveMap';
const ACTION_SWITCH_MAP = 'MapList.SwitchMap';
const ACTION_START_SWITCH_VOTE = 'MapList.StartMapSwitchVote';
const ACTION_QUEUED_MAP = 'MapList.QueueMap';
const ACTION_UNQUEUE_MAP = 'MapList.UnQueueMap';
const ACTION_CHECK_UPDATE = 'MapList.CheckUpdate';
const ACTION_CLEAR_MAPQUEUE = 'MapList.ClearMapQueue';
const ACTION_PAGING_CHUNKS = 'MapList.PagingChunk.';
const MAX_MAPS_PER_PAGE = 15;
const MAX_PAGES_PER_CHUNK = 2;
const DEFAULT_KARMA_PLUGIN = 'MCTeam\KarmaPlugin';
const DEFAULT_CUSTOM_VOTE_PLUGIN = 'MCTeam\CustomVotesPlugin';
const CACHE_CURRENT_PAGE = 'CurrentPage';
const WIDGET_NAME = 'MapList';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/**
* Construct a new map list instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(ManialinkManager::CB_MAIN_WINDOW_CLOSED, $this, 'closeWidget');
$this->maniaControl->getCallbackManager()->registerCallbackListener(ManialinkManager::CB_MAIN_WINDOW_OPENED, $this, 'handleWidgetOpened');
$this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_PLAYERMANIALINKPAGEANSWER, $this, 'handleManialinkPageAnswer');
$this->maniaControl->getCallbackManager()->registerCallbackListener(MapQueue::CB_MAPQUEUE_CHANGED, $this, 'updateWidget');
$this->maniaControl->getCallbackManager()->registerCallbackListener(MapManager::CB_MAPS_UPDATED, $this, 'updateWidget');
$this->maniaControl->getCallbackManager()->registerCallbackListener(MapManager::CB_KARMA_UPDATED, $this, 'updateWidget');
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::BEGINMAP, $this, 'updateWidget');
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ACTION_CHECK_UPDATE, $this, 'checkUpdates');
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ACTION_CLEAR_MAPQUEUE, $this, 'clearMapQueue');
}
/**
* Clear the Map Queue
*
* @param array $chatCallback
* @param Player $player
*/
public function clearMapQueue(array $chatCallback, Player $player) {
// Clears the Map Queue
$this->maniaControl->getMapManager()->getMapQueue()->clearMapQueue($player);
}
/**
* Check for Map Updates
*
* @param array $chatCallback
* @param Player $player
*/
public function checkUpdates(array $chatCallback, Player $player) {
// Update Mx Infos
$this->maniaControl->getMapManager()->getMXManager()->fetchManiaExchangeMapInformation();
// Reshow the Maplist
$this->showMapList($player);
}
/**
* Display a MapList on the Screen
*
* @param Player $player
* @param Map[] $mapList
* @param int $pageIndex
*/
public function showMapList(Player $player, $mapList = null, $pageIndex = -1) {
$width = $this->maniaControl->getManialinkManager()->getStyleManager()->getListWidgetsWidth();
$height = $this->maniaControl->getManialinkManager()->getStyleManager()->getListWidgetsHeight();
if ($pageIndex < 0) {
$pageIndex = (int)$player->getCache($this, self::CACHE_CURRENT_PAGE);
}
$player->setCache($this, self::CACHE_CURRENT_PAGE, $pageIndex);
$queueBuffer = $this->maniaControl->getMapManager()->getMapQueue()->getQueueBuffer();
$chunkIndex = $this->getChunkIndexFromPageNumber($pageIndex);
$mapsBeginIndex = $this->getChunkMapsBeginIndex($chunkIndex);
// Get Maps
if (!is_array($mapList)) {
$mapList = $this->maniaControl->getMapManager()->getMaps();
}
$mapList = array_slice($mapList, $mapsBeginIndex, self::MAX_PAGES_PER_CHUNK * self::MAX_MAPS_PER_PAGE);
$totalMapsCount = $this->maniaControl->getMapManager()->getMapsCount();
$pagesCount = ceil($totalMapsCount / self::MAX_MAPS_PER_PAGE);
// Create ManiaLink
$maniaLink = new ManiaLink(ManialinkManager::MAIN_MLID);
$script = $maniaLink->getScript();
$paging = new Paging();
$script->addFeature($paging);
$paging->setCustomMaxPageNumber($pagesCount);
$paging->setChunkActionAppendsPageNumber(true);
$paging->setChunkActions(self::ACTION_PAGING_CHUNKS);
// Main frame
$frame = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultListFrame($script, $paging);
$maniaLink->add($frame);
// Admin Buttons
if ($this->maniaControl->getAuthenticationManager()->checkPermission($player, MapQueue::SETTING_PERMISSION_CLEAR_MAPQUEUE)
) {
// Clear Map-Queue
$label = new Label_Button();
$frame->add($label);
$label->setText('Clear Map-Queue');
$label->setTextSize(1);
$label->setPosition($width / 2 - 8, -$height / 2 + 9);
$label->setHAlign($label::RIGHT);
$quad = new Quad_BgsPlayerCard();
$frame->add($quad);
$quad->setPosition($width / 2 - 5, -$height / 2 + 9, 0.01);
$quad->setSubStyle($quad::SUBSTYLE_BgPlayerCardBig);
$quad->setHAlign($quad::RIGHT);
$quad->setSize(29, 4);
$quad->setAction(self::ACTION_CLEAR_MAPQUEUE);
}
if ($this->maniaControl->getAuthenticationManager()->checkPermission($player, MapManager::SETTING_PERMISSION_CHECK_UPDATE)
) {
// Check Update
$label = new Label_Button();
$frame->add($label);
$label->setText('Check MX for Updates');
$label->setTextSize(1);
$label->setPosition($width / 2 - 41, -$height / 2 + 9, 0.01);
$label->setHAlign($label::RIGHT);
$quad = new Quad_BgsPlayerCard();
$frame->add($quad);
$quad->setPosition($width / 2 - 37, -$height / 2 + 9, 0.01);
$quad->setSubStyle($quad::SUBSTYLE_BgPlayerCardBig);
$quad->setHAlign($quad::RIGHT);
$quad->setSize(35, 4);
$quad->setAction(self::ACTION_CHECK_UPDATE);
$mxQuad = new Quad();
$frame->add($mxQuad);
$mxQuad->setSize(3, 3);
$mxQuad->setImage($this->maniaControl->getManialinkManager()->getIconManager()->getIcon(IconManager::MX_ICON_GREEN));
$mxQuad->setImageFocus($this->maniaControl->getManialinkManager()->getIconManager()->getIcon(IconManager::MX_ICON_GREEN_MOVER));
$mxQuad->setPosition($width / 2 - 67, -$height / 2 + 9);
$mxQuad->setZ(0.01);
$mxQuad->setAction(self::ACTION_CHECK_UPDATE);
}
if ($this->maniaControl->getAuthenticationManager()->checkPermission($player, MapManager::SETTING_PERMISSION_ADD_MAP)
) {
// Directory browser
$browserButton = new Label_Button();
$frame->add($browserButton);
$browserButton->setPosition($width / -2 + 20, -$height / 2 + 9, 0.01);
$browserButton->setTextSize(1);
$browserButton->setText('Directory Browser');
$browserQuad = new Quad_BgsPlayerCard();
$frame->add($browserQuad);
$browserQuad->setPosition($width / -2 + 20, -$height / 2 + 9, 0.01);
$browserQuad->setSubStyle($browserQuad::SUBSTYLE_BgPlayerCardBig);
$browserQuad->setSize(35, 4);
$browserQuad->setAction(DirectoryBrowser::ACTION_SHOW);
}
// Headline
$headFrame = new Frame();
$frame->add($headFrame);
$headFrame->setY($height / 2 - 5);
$posX = -$width / 2;
$array = array('Id' => $posX + 5, 'Mx Id' => $posX + 10, 'Map Name' => $posX + 20, 'Author' => $posX + 68, 'Karma' => $posX + 115, 'Actions' => $width / 2 - 16);
$this->maniaControl->getManialinkManager()->labelLine($headFrame, $array);
// Predefine description Label
$descriptionLabel = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultDescriptionLabel();
$frame->add($descriptionLabel);
$queuedMaps = $this->maniaControl->getMapManager()->getMapQueue()->getQueuedMapsRanking();
/** @var KarmaPlugin $karmaPlugin */
$karmaPlugin = $this->maniaControl->getPluginManager()->getPlugin(self::DEFAULT_KARMA_PLUGIN);
$pageNumber = 1 + $chunkIndex * self::MAX_PAGES_PER_CHUNK;
$paging->setStartPageNumber($pageIndex + 1);
$index = 0;
$mapListId = 1 + $mapsBeginIndex;
$posY = $height / 2 - 10;
$pageFrame = null;
$currentMap = $this->maniaControl->getMapManager()->getCurrentMap();
$mxIcon = $this->maniaControl->getManialinkManager()->getIconManager()->getIcon(IconManager::MX_ICON);
$mxIconHover = $this->maniaControl->getManialinkManager()->getIconManager()->getIcon(IconManager::MX_ICON_MOVER);
$mxIconGreen = $this->maniaControl->getManialinkManager()->getIconManager()->getIcon(IconManager::MX_ICON_GREEN);
$mxIconGreenHover = $this->maniaControl->getManialinkManager()->getIconManager()->getIcon(IconManager::MX_ICON_GREEN_MOVER);
foreach ($mapList as $map) {
/** @var Map $map */
if ($index % self::MAX_MAPS_PER_PAGE === 0) {
$pageFrame = new Frame();
$frame->add($pageFrame);
$posY = $height / 2 - 10;
$paging->addPage($pageFrame, $pageNumber);
$pageNumber++;
}
// Map Frame
$mapFrame = new Frame();
$pageFrame->add($mapFrame);
$mapFrame->setY($posY);
$mapFrame->setZ(0.1);
if ($mapListId % 2 !== 0) {
$lineQuad = new Quad_BgsPlayerCard();
$mapFrame->add($lineQuad);
$lineQuad->setSize($width, 4);
$lineQuad->setSubStyle($lineQuad::SUBSTYLE_BgPlayerCardBig);
$lineQuad->setZ(0.001);
}
if ($currentMap === $map) {
$currentQuad = new Quad_Icons64x64_1();
$mapFrame->add($currentQuad);
$currentQuad->setX($posX + 3.5);
$currentQuad->setZ(0.2);
$currentQuad->setSize(4, 4);
$currentQuad->setSubStyle($currentQuad::SUBSTYLE_ArrowBlue);
}
$mxId = '-';
if (isset($map->mx->id)) {
$mxId = $map->mx->id;
$mxQuad = new Quad();
$mapFrame->add($mxQuad);
$mxQuad->setSize(3, 3);
$mxQuad->setImage($mxIcon);
$mxQuad->setImageFocus($mxIconHover);
$mxQuad->setX($posX + 65);
$mxQuad->setUrl($map->mx->pageurl);
$mxQuad->setZ(0.01);
$description = 'View ' . $map->getEscapedName() . ' on Mania-Exchange';
$mxQuad->addTooltipLabelFeature($descriptionLabel, $description);
if ($map->updateAvailable()) {
$mxQuad = new Quad();
$mapFrame->add($mxQuad);
$mxQuad->setSize(3, 3);
$mxQuad->setImage($mxIconGreen);
$mxQuad->setImageFocus($mxIconGreenHover);
$mxQuad->setX($posX + 62);
$mxQuad->setUrl($map->mx->pageurl);
$mxQuad->setZ(0.01);
$description = 'Update for ' . $map->getEscapedName() . ' available on Mania-Exchange!';
$mxQuad->addTooltipLabelFeature($descriptionLabel, $description);
// Update Button
if ($this->maniaControl->getAuthenticationManager()->checkPermission($player, MapManager::SETTING_PERMISSION_ADD_MAP)
) {
$mxQuad->setAction(self::ACTION_UPDATE_MAP . '.' . $map->uid);
}
}
}
// Display Maps
$array = array($mapListId => $posX + 5, $mxId => $posX + 10, Formatter::stripDirtyCodes($map->name) => $posX + 20, $map->authorNick => $posX + 68);
$labels = $this->maniaControl->getManialinkManager()->labelLine($mapFrame, $array);
if (isset($labels[3])) {
/** @var Label $label */
$label = $labels[3];
$description = 'Click to checkout all maps by $<' . $map->authorLogin . '$>!';
$label->setAction(MapCommands::ACTION_SHOW_AUTHOR . $map->authorLogin);
$label->addTooltipLabelFeature($descriptionLabel, $description);
}
// TODO action detailed map info including mx info
// Map-Queue-Map-Label
if (isset($queuedMaps[$map->uid])) {
$label = new Label_Text();
$mapFrame->add($label);
$label->setX($width / 2 - 13);
$label->setZ(0.2);
$label->setTextSize(1.5);
$label->setText($queuedMaps[$map->uid]);
$label->setTextColor('fff');
// Checks if the Player who opened the Widget has queued the map
$queuer = $this->maniaControl->getMapManager()->getMapQueue()->getQueuer($map->uid);
if ($queuer && $queuer->login == $player->login) {
$description = 'Remove ' . $map->getEscapedName() . ' from the Map Queue';
$label->addTooltipLabelFeature($descriptionLabel, $description);
$label->setAction(self::ACTION_UNQUEUE_MAP . '.' . $map->uid);
} else {
$description = $map->getEscapedName() . ' is on Map-Queue Position: ' . $queuedMaps[$map->uid];
$label->addTooltipLabelFeature($descriptionLabel, $description);
}
} else {
// Map-Queue-Map-Button
$queueLabel = new Label_Button();
$mapFrame->add($queueLabel);
$queueLabel->setX($width / 2 - 13);
$queueLabel->setZ(0.2);
$queueLabel->setSize(3, 3);
$queueLabel->setText('+');
if (in_array($map->uid, $queueBuffer)) {
if ($this->maniaControl->getAuthenticationManager()->checkPermission($player, MapQueue::SETTING_PERMISSION_CLEAR_MAPQUEUE)
) {
$queueLabel->setAction(self::ACTION_QUEUED_MAP . '.' . $map->uid);
}
$queueLabel->setTextColor('f00');
$description = $map->getEscapedName() . ' has recently been played!';
$queueLabel->addTooltipLabelFeature($descriptionLabel, $description);
} else {
$queueLabel->setTextColor('09f');
$queueLabel->setAction(self::ACTION_QUEUED_MAP . '.' . $map->uid);
$description = 'Add ' . $map->getEscapedName() . ' to the Map Queue';
$queueLabel->addTooltipLabelFeature($descriptionLabel, $description);
}
}
if ($this->maniaControl->getAuthenticationManager()->checkPermission($player, MapManager::SETTING_PERMISSION_REMOVE_MAP)
) {
// remove map button
$removeButton = new Label_Button();
$mapFrame->add($removeButton);
$removeButton->setX($width / 2 - 5);
$removeButton->setZ(0.2);
$removeButton->setSize(3, 3);
$removeButton->setTextSize(1);
$removeButton->setText('x');
$removeButton->setTextColor('a00');
$confirmFrame = $this->buildConfirmFrame($maniaLink, $posY, $map->uid, true);
$removeButton->addToggleFeature($confirmFrame);
$description = 'Remove Map: ' . $map->getEscapedName();
$removeButton->addTooltipLabelFeature($descriptionLabel, $description);
}
if ($this->maniaControl->getAuthenticationManager()->checkPermission($player, MapManager::SETTING_PERMISSION_ADD_MAP)
) {
// Switch to button
$switchLabel = new Label_Button();
$mapFrame->add($switchLabel);
$switchLabel->setX($width / 2 - 9);
$switchLabel->setZ(0.2);
$switchLabel->setSize(3, 3);
$switchLabel->setTextSize(2);
$switchLabel->setText('»');
$switchLabel->setTextColor('0f0');
$confirmFrame = $this->buildConfirmFrame($maniaLink, $posY, $map->uid);
$switchLabel->addToggleFeature($confirmFrame);
$description = 'Switch Directly to Map: ' . $map->getEscapedName();
$switchLabel->addTooltipLabelFeature($descriptionLabel, $description);
}
if ($this->maniaControl->getPluginManager()->isPluginActive(self::DEFAULT_CUSTOM_VOTE_PLUGIN)
) {
if ($this->maniaControl->getAuthenticationManager()->checkPermission($player, MapManager::SETTING_PERMISSION_ADD_MAP)
) {
// Switch Map Voting for Admins
$switchQuad = new Quad_UIConstruction_Buttons();
$mapFrame->add($switchQuad);
$switchQuad->setX($width / 2 - 17);
$switchQuad->setZ(0.2);
$switchQuad->setSubStyle($switchQuad::SUBSTYLE_Validate_Step2);
$switchQuad->setSize(3.8, 3.8);
$switchQuad->setAction(self::ACTION_START_SWITCH_VOTE . '.' . $map->uid);
$description = 'Start Map-Switch Vote: $<' . $map->name . '$>';
$switchQuad->addTooltipLabelFeature($descriptionLabel, $description);
} else {
// Switch Map Voting for Player
$switchLabel = new Label_Button();
$mapFrame->add($switchLabel);
$switchLabel->setX($width / 2 - 7);
$switchLabel->setZ(0.2);
$switchLabel->setSize(3, 3);
$switchLabel->setTextSize(2);
$switchLabel->setText('»');
$switchLabel->setTextColor('0f0');
$switchLabel->setAction(self::ACTION_START_SWITCH_VOTE . '.' . $map->uid);
$description = 'Start Map-Switch Vote: ' . $map->getEscapedName();
$switchLabel->addTooltipLabelFeature($descriptionLabel, $description);
}
}
// Display Karma bar
if ($karmaPlugin) {
$karma = $karmaPlugin->getMapKarma($map);
$votes = $karmaPlugin->getMapVotes($map);
if (is_numeric($karma)) {
if ($this->maniaControl->getSettingManager()->getSettingValue($karmaPlugin, $karmaPlugin::SETTING_NEWKARMA)
) {
$karmaText = ' ' . round($karma * 100.) . '% (' . $votes['count'] . ')';
} else {
$min = 0;
$plus = 0;
foreach ($votes as $vote) {
if (isset($vote->vote)) {
if ($vote->vote !== 0.5) {
if ($vote->vote < 0.5) {
$min = $min + $vote->count;
} else {
$plus = $plus + $vote->count;
}
}
}
}
$endKarma = $plus - $min;
$karmaText = ' ' . $endKarma . ' (' . $votes['count'] . 'x / ' . round($karma * 100.) . '%)';
}
$karmaGauge = new Gauge();
$mapFrame->add($karmaGauge);
$karmaGauge->setZ(2);
$karmaGauge->setX($posX + 120);
$karmaGauge->setSize(20, 9);
$karmaGauge->setDrawBg(false);
$karma = floatval($karma);
$karmaGauge->setRatio($karma + 0.15 - $karma * 0.15);
$karmaColor = ColorUtil::floatToStatusColor($karma);
$karmaGauge->setColor($karmaColor . '9');
$karmaLabel = new Label();
$mapFrame->add($karmaLabel);
$karmaLabel->setZ(2);
$karmaLabel->setX($posX + 120);
$karmaLabel->setSize(20 * 0.9, 5);
$karmaLabel->setTextSize(0.9);
$karmaLabel->setTextColor('000');
$karmaLabel->setText($karmaText);
}
}
$posY -= 4;
$mapListId++;
$index++;
}
$this->maniaControl->getManialinkManager()->displayWidget($maniaLink, $player, self::WIDGET_NAME);
}
/**
* Get the Chunk Index with the given Page Index
*
* @param int $pageIndex
* @return int
*/
private function getChunkIndexFromPageNumber($pageIndex) {
$mapsCount = $this->maniaControl->getMapManager()->getMapsCount();
$pagesCount = ceil($mapsCount / self::MAX_MAPS_PER_PAGE);
if ($pageIndex > $pagesCount - 1) {
$pageIndex = $pagesCount - 1;
}
return floor($pageIndex / self::MAX_PAGES_PER_CHUNK);
}
/**
* Calculate the First Map Index to show for the given Chunk
*
* @param int $chunkIndex
* @return int
*/
private function getChunkMapsBeginIndex($chunkIndex) {
return $chunkIndex * self::MAX_PAGES_PER_CHUNK * self::MAX_MAPS_PER_PAGE;
}
/**
* Builds the confirmation frame
*
* @param ManiaLink $maniaLink
* @param float $posY
* @param bool $mapUid
* @param bool $remove
* @return Frame
*/
public function buildConfirmFrame(Manialink $maniaLink, $posY, $mapUid, $remove = false) {
// TODO: get rid of the confirm frame to decrease xml size & network usage
// SUGGESTION: just send them as own manialink again on clicking?
$width = $this->maniaControl->getManialinkManager()->getStyleManager()->getListWidgetsWidth();
$quadStyle = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultMainWindowStyle();
$quadSubstyle = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultMainWindowSubStyle();
$confirmFrame = new Frame();
$maniaLink->add($confirmFrame);
$confirmFrame->setPosition($width / 2 + 6, $posY);
$confirmFrame->setVisible(false);
$quad = new Quad();
$confirmFrame->add($quad);
$quad->setStyles($quadStyle, $quadSubstyle);
$quad->setSize(12, 4);
$quad = new Quad_BgsPlayerCard();
$confirmFrame->add($quad);
$quad->setSubStyle($quad::SUBSTYLE_BgCardSystem);
$quad->setSize(11, 3.5);
$label = new Label_Button();
$confirmFrame->add($label);
$label->setText('Sure?');
$label->setTextSize(1);
$label->setScale(0.90);
$label->setX(-1.3);
$buttLabel = new Label_Button();
$confirmFrame->add($buttLabel);
$buttLabel->setPosition(3.2, 0.4, 0.2);
$buttLabel->setSize(3, 3);
if ($remove) {
$buttLabel->setTextSize(1);
$buttLabel->setTextColor('a00');
$buttLabel->setText('x');
$quad->setAction(self::ACTION_REMOVE_MAP . '.' . $mapUid);
} else {
$buttLabel->setTextSize(2);
$buttLabel->setTextColor('0f0');
$buttLabel->setText('»');
$quad->setAction(self::ACTION_SWITCH_MAP . '.' . $mapUid);
}
return $confirmFrame;
}
/**
* Unset the player if he opened another Main Widget
*
* @param Player $player
* @param string $openedWidget
*/
public function handleWidgetOpened(Player $player, $openedWidget) {
// unset when another main widget got opened
if ($openedWidget !== self::WIDGET_NAME) {
$player->destroyCache($this, self::CACHE_CURRENT_PAGE);
}
}
/**
* Close the widget
*
* @param Player $player
*/
public function closeWidget(Player $player) {
// TODO: resolve duplicate with 'playerCloseWidget'
$player->destroyCache($this, self::CACHE_CURRENT_PAGE);
}
/**
* Handle ManialinkPageAnswer Callback
*
* @param array $callback
*/
public function handleManialinkPageAnswer(array $callback) {
$actionId = $callback[1][2];
$actionArray = explode('.', $actionId);
if (count($actionArray) <= 2) {
return;
}
$action = $actionArray[0] . '.' . $actionArray[1];
$login = $callback[1][1];
$player = $this->maniaControl->getPlayerManager()->getPlayer($login);
$mapUid = $actionArray[2];
switch ($action) {
case self::ACTION_UPDATE_MAP:
$this->maniaControl->getMapManager()->updateMap($player, $mapUid);
$this->showMapList($player);
break;
case self::ACTION_REMOVE_MAP:
try {
$this->maniaControl->getMapManager()->removeMap($player, $mapUid);
} catch (FileException $e) {
$this->maniaControl->getChat()->sendException($e, $player);
}
break;
case self::ACTION_SWITCH_MAP:
// Don't queue on Map-Change
$this->maniaControl->getMapManager()->getMapQueue()->dontQueueNextMapChange();
try {
$this->maniaControl->getClient()->jumpToMapIdent($mapUid);
} catch (NextMapException $exception) {
$this->maniaControl->getChat()->sendError('Error on Jumping to Map Ident: ' . $exception->getMessage(), $player);
break;
} catch (NotInListException $exception) {
// TODO: "Map not found." -> how is that possible?
$this->maniaControl->getChat()->sendError('Error on Jumping to Map Ident: ' . $exception->getMessage(), $player);
break;
}
$map = $this->maniaControl->getMapManager()->getMapByUid($mapUid);
$message = $player->getEscapedNickname() . ' skipped to Map $z' . $map->getEscapedName() . '!';
$this->maniaControl->getChat()->sendSuccess($message);
Logger::logInfo($message, true);
$this->playerCloseWidget($player);
break;
case self::ACTION_START_SWITCH_VOTE:
/** @var CustomVotesPlugin $votesPlugin */
$votesPlugin = $this->maniaControl->getPluginManager()->getPlugin(self::DEFAULT_CUSTOM_VOTE_PLUGIN);
$map = $this->maniaControl->getMapManager()->getMapByUid($mapUid);
$message = $player->getEscapedNickname() . '$s started a vote to switch to ' . $map->getEscapedName() . '!';
$votesPlugin->defineVote('switchmap', 'Goto ' . $map->name, true, $message)->setStopCallback(Callbacks::ENDMAP);
$votesPlugin->startVote($player, 'switchmap', function ($result) use (&$votesPlugin, &$map) {
$votesPlugin->undefineVote('switchmap');
//Don't queue on Map-Change
$this->maniaControl->getMapManager()->getMapQueue()->dontQueueNextMapChange();
try {
$this->maniaControl->getClient()->JumpToMapIdent($map->uid);
} catch (NextMapException $exception) {
return;
} catch (NotInListException $exception) {
return;
} catch (ChangeInProgressException $exception) {
// TODO: delay skip if change is in progress
return;
}
$this->maniaControl->getChat()->sendInformation('$sVote Successful -> Map switched!');
});
break;
case self::ACTION_QUEUED_MAP:
$this->maniaControl->getMapManager()->getMapQueue()->addMapToMapQueue($callback[1][1], $mapUid);
$this->showMapList($player);
break;
case self::ACTION_UNQUEUE_MAP:
$this->maniaControl->getMapManager()->getMapQueue()->removeFromMapQueue($player, $mapUid);
$this->showMapList($player);
break;
default:
if (substr($actionId, 0, strlen(self::ACTION_PAGING_CHUNKS)) === self::ACTION_PAGING_CHUNKS) {
// Paging chunks
$neededPage = (int)substr($actionId, strlen(self::ACTION_PAGING_CHUNKS));
$this->showMapList($player, null, $neededPage - 1);
}
break;
}
}
/**
* Close the widget for
*
* @param Player $player
*/
public function playerCloseWidget(Player $player) {
$player->destroyCache($this, self::CACHE_CURRENT_PAGE);
$this->maniaControl->getManialinkManager()->closeWidget($player);
}
/**
* Reopen the widget on Map Begin, MapListChanged, etc.
*/
public function updateWidget() {
$players = $this->maniaControl->getPlayerManager()->getPlayers();
foreach ($players as $player) {
$currentPage = $player->getCache($this, self::CACHE_CURRENT_PAGE);
if ($currentPage !== null) {
$this->showMapList($player, null, $currentPage);
}
}
}
}

869
core/Maps/MapManager.php Normal file
View File

@ -0,0 +1,869 @@
<?php
namespace ManiaControl\Maps;
use ManiaControl\Admin\AuthenticationManager;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\CallbackManager;
use ManiaControl\Callbacks\Callbacks;
use ManiaControl\Files\FileUtil;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
use ManiaControl\ManiaExchange\ManiaExchangeList;
use ManiaControl\ManiaExchange\ManiaExchangeManager;
use ManiaControl\ManiaExchange\MXMapInfo;
use ManiaControl\Players\Player;
use ManiaControl\Utils\Formatter;
use Maniaplanet\DedicatedServer\InvalidArgumentException;
use Maniaplanet\DedicatedServer\Xmlrpc\AlreadyInListException;
use Maniaplanet\DedicatedServer\Xmlrpc\Exception;
use Maniaplanet\DedicatedServer\Xmlrpc\FileException;
use Maniaplanet\DedicatedServer\Xmlrpc\IndexOutOfBoundException;
use Maniaplanet\DedicatedServer\Xmlrpc\InvalidMapException;
use Maniaplanet\DedicatedServer\Xmlrpc\NotInListException;
use Maniaplanet\DedicatedServer\Xmlrpc\UnavailableFeatureException;
/**
* ManiaControl Map Manager Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class MapManager implements CallbackListener {
/*
* Constants
*/
const TABLE_MAPS = 'mc_maps';
const CB_MAPS_UPDATED = 'MapManager.MapsUpdated';
const CB_KARMA_UPDATED = 'MapManager.KarmaUpdated';
const SETTING_PERMISSION_ADD_MAP = 'Add Maps';
const SETTING_PERMISSION_REMOVE_MAP = 'Remove Maps';
const SETTING_PERMISSION_ERASE_MAP = 'Erase Maps';
const SETTING_PERMISSION_SHUFFLE_MAPS = 'Shuffle Maps';
const SETTING_PERMISSION_CHECK_UPDATE = 'Check Map Update';
const SETTING_PERMISSION_SKIP_MAP = 'Skip Map';
const SETTING_PERMISSION_RESTART_MAP = 'Restart Map';
const SETTING_AUTOSAVE_MAPLIST = 'Autosave Maplist file';
const SETTING_MAPLIST_FILE = 'File to write Maplist in';
const SETTING_WRITE_OWN_MAPLIST_FILE = 'Write a own Maplist File for every Server called serverlogin.txt';
/*
* Public properties
*/
/** @var MapQueue $mapQueue */
/** @deprecated see getMapQueue() */
public $mapQueue = null;
/** @var MapCommands $mapCommands */
/** @deprecated see getMapCommands() */
public $mapCommands = null;
/** @var MapActions $mapActions */
/** @deprecated see getMapActions() */
public $mapActions = null;
/** @var MapList $mapList */
/** @deprecated see getMapList() */
public $mapList = null;
/** @var DirectoryBrowser $directoryBrowser */
/** @deprecated see getDirectoryBrowser() */
public $directoryBrowser = null;
/** @var ManiaExchangeList $mxList */
/** @deprecated see getMXList() */
public $mxList = null;
/** @var ManiaExchangeManager $mxManager */
/** @deprecated see getMXManager() */
public $mxManager = null;
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/** @var Map[] $maps */
private $maps = array();
/** @var Map $currentMap */
private $currentMap = null;
private $mapEnded = false;
private $mapBegan = false;
/**
* Construct a new map manager instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
$this->initTables();
// Children
$this->mxManager = new ManiaExchangeManager($this->maniaControl);
$this->mapList = new MapList($this->maniaControl);
$this->directoryBrowser = new DirectoryBrowser($this->maniaControl);
$this->mxList = new ManiaExchangeList($this->maniaControl);
$this->mapCommands = new MapCommands($maniaControl);
$this->mapQueue = new MapQueue($this->maniaControl);
$this->mapActions = new MapActions($maniaControl);
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::ONINIT, $this, 'handleOnInit');
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::AFTERINIT, $this, 'handleAfterInit');
$this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_MAPLISTMODIFIED, $this, 'mapsModified');
// Permissions
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_ADD_MAP, AuthenticationManager::AUTH_LEVEL_ADMIN);
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_REMOVE_MAP, AuthenticationManager::AUTH_LEVEL_ADMIN);
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_ERASE_MAP, AuthenticationManager::AUTH_LEVEL_SUPERADMIN);
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_SHUFFLE_MAPS, AuthenticationManager::AUTH_LEVEL_ADMIN);
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_CHECK_UPDATE, AuthenticationManager::AUTH_LEVEL_MODERATOR);
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_SKIP_MAP, AuthenticationManager::AUTH_LEVEL_MODERATOR);
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_RESTART_MAP, AuthenticationManager::AUTH_LEVEL_MODERATOR);
// Settings
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_AUTOSAVE_MAPLIST, true);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_MAPLIST_FILE, "MatchSettings/tracklist.txt");
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_WRITE_OWN_MAPLIST_FILE, false);
}
/**
* Initialize necessary database tables
*
* @return bool
*/
private function initTables() {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$query = "CREATE TABLE IF NOT EXISTS `" . self::TABLE_MAPS . "` (
`index` int(11) NOT NULL AUTO_INCREMENT,
`mxid` int(11),
`uid` varchar(50) NOT NULL,
`name` varchar(150) NOT NULL,
`authorLogin` varchar(100) NOT NULL,
`fileName` varchar(100) NOT NULL,
`environment` varchar(50) NOT NULL,
`mapType` varchar(50) NOT NULL,
`changed` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`index`),
UNIQUE KEY `uid` (`uid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Map Data' AUTO_INCREMENT=1;";
$result = $mysqli->query($query);
if ($mysqli->error) {
trigger_error($mysqli->error, E_USER_ERROR);
return false;
}
return $result;
}
/**
* Return the map commands
*
* @return MapCommands
*/
public function getMapCommands() {
return $this->mapCommands;
}
/**
* Return the map actions
*
* @return MapActions
*/
public function getMapActions() {
return $this->mapActions;
}
/**
* Return the map list
*
* @return MapList
*/
public function getMapList() {
return $this->mapList;
}
/**
* Return the directory browser
*
* @return DirectoryBrowser
*/
public function getDirectoryBrowser() {
return $this->directoryBrowser;
}
/**
* Return the mx list
*
* @return ManiaExchangeList
*/
public function getMXList() {
return $this->mxList;
}
/**
* Update a Map from Mania Exchange
*
* @param Player $admin
* @param string $uid
*/
public function updateMap(Player $admin, $uid) {
$this->updateMapTimestamp($uid);
if (!isset($uid) || !isset($this->maps[$uid])) {
$this->maniaControl->getChat()->sendError("Error updating Map: Unknown UID '{$uid}'!", $admin);
return;
}
/** @var Map $map */
$map = $this->maps[$uid];
$mxId = $map->mx->id;
$this->removeMap($admin, $uid, true, false);
$this->addMapFromMx($mxId, $admin->login, true);
}
/**
* Update the Timestamp of a Map
*
* @param string $uid
* @return bool
*/
private function updateMapTimestamp($uid) {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$mapQuery = "UPDATE `" . self::TABLE_MAPS . "` SET
mxid = 0,
changed = NOW()
WHERE 'uid' = ?";
$mapStatement = $mysqli->prepare($mapQuery);
if ($mysqli->error) {
trigger_error($mysqli->error);
return false;
}
$mapStatement->bind_param('s', $uid);
$mapStatement->execute();
if ($mapStatement->error) {
trigger_error($mapStatement->error);
$mapStatement->close();
return false;
}
$mapStatement->close();
return true;
}
/**
* Remove a Map
*
* @param Player $admin
* @param string $uid
* @param bool $eraseFile
* @param bool $message
*/
public function removeMap(Player $admin, $uid, $eraseFile = false, $message = true) {
if (!isset($this->maps[$uid])) {
$this->maniaControl->getChat()->sendError('Map does not exist!', $admin);
return;
}
/** @var Map $map */
$map = $this->maps[$uid];
// Unset the Map everywhere
$this->getMapQueue()->removeFromMapQueue($admin, $map->uid);
if ($map->mx) {
$this->getMXManager()->unsetMap($map->mx->id);
}
// Remove map
try {
$this->maniaControl->getClient()->removeMap($map->fileName);
} catch (NotInListException $e) {
} catch (FileException $e) {
}
unset($this->maps[$uid]);
if ($eraseFile) {
// Check if ManiaControl can even write to the maps dir
$mapDir = $this->maniaControl->getClient()->getMapsDirectory();
if ($this->maniaControl->getServer()->checkAccess($mapDir)
) {
// Delete map file
if (!@unlink($mapDir . $map->fileName)) {
$this->maniaControl->getChat()->sendError("Couldn't erase the map file.", $admin);
$eraseFile = false;
}
} else {
$this->maniaControl->getChat()->sendError("Couldn't erase the map file (no access).", $admin);
$eraseFile = false;
}
}
// Show Message
if ($message) {
$action = ($eraseFile ? 'erased' : 'removed');
$message = $admin->getEscapedNickname() . ' ' . $action . ' ' . $map->getEscapedName() . '!';
$this->maniaControl->getChat()->sendSuccess($message);
Logger::logInfo($message, true);
}
}
/**
* Return the map queue
*
* @return MapQueue
*/
public function getMapQueue() {
return $this->mapQueue;
}
/**
* Return the mx manager
*
* @return ManiaExchangeManager
*/
public function getMXManager() {
return $this->mxManager;
}
/**
* Adds a Map from Mania Exchange
*
* @param int $mapId
* @param string $login
* @param bool $update
*/
public function addMapFromMx($mapId, $login, $update = false) {
if (is_numeric($mapId)) {
// Check if map exists
$this->maniaControl->getMapManager()->getMXManager()->fetchMapInfo($mapId, function (MXMapInfo $mapInfo = null) use (
&$login, &$update
) {
if (!$mapInfo || !isset($mapInfo->uploaded)) {
// Invalid id
$this->maniaControl->getChat()->sendError('Invalid MX-Id!', $login);
return;
}
// Download the file
$this->maniaControl->getFileReader()->loadFile($mapInfo->downloadurl, function ($file, $error) use (
&$login, &$mapInfo, &$update
) {
if (!$file || $error) {
// Download error
$this->maniaControl->getChat()->sendError("Download failed: '{$error}'!", $login);
return;
}
$this->processMapFile($file, $mapInfo, $login, $update);
});
});
}
}
/**
* Process the MapFile
*
* @param string $file
* @param MXMapInfo $mapInfo
* @param string $login
* @param bool $update
* @throws InvalidArgumentException
*/
private function processMapFile($file, MXMapInfo $mapInfo, $login, $update) {
// Check if map is already on the server
if ($this->getMapByUid($mapInfo->uid)) {
$this->maniaControl->getChat()->sendError('Map is already on the server!', $login);
return;
}
// Save map
$fileName = $mapInfo->id . '_' . $mapInfo->name . '.Map.Gbx';
$fileName = FileUtil::getClearedFileName($fileName);
$downloadFolderName = $this->maniaControl->getSettingManager()->getSettingValue($this, 'MapDownloadDirectory', 'MX');
$relativeMapFileName = $downloadFolderName . DIRECTORY_SEPARATOR . $fileName;
$mapDir = $this->maniaControl->getServer()->getDirectory()->getMapsFolder();
$downloadDirectory = $mapDir . $downloadFolderName . DIRECTORY_SEPARATOR;
$fullMapFileName = $downloadDirectory . $fileName;
// Check if it can get written locally
if ($this->maniaControl->getServer()->checkAccess($mapDir)) {
// Create download directory if necessary
if (!is_dir($downloadDirectory) && !mkdir($downloadDirectory) || !is_writable($downloadDirectory)) {
$this->maniaControl->getChat()->sendError("ManiaControl doesn't have to rights to save maps in '{$downloadDirectory}'.", $login);
return;
}
if (!file_put_contents($fullMapFileName, $file)) {
// Save error
$this->maniaControl->getChat()->sendError('Saving map failed!', $login);
return;
}
} else {
// Write map via write file method
try {
$this->maniaControl->getClient()->writeFile($relativeMapFileName, $file);
} catch (InvalidArgumentException $e) {
if ($e->getMessage() === 'data are too big') {
$this->maniaControl->getChat()->sendError("Map is too big for a remote save.", $login);
return;
}
throw $e;
}
}
// Check for valid map
try {
$this->maniaControl->getClient()->checkMapForCurrentServerParams($relativeMapFileName);
} catch (InvalidMapException $exception) {
$this->maniaControl->getChat()->sendException($exception, $login);
return;
} catch (FileException $exception) {
$this->maniaControl->getChat()->sendException($exception, $login);
return;
}
// Add map to map list
try {
$this->maniaControl->getClient()->insertMap($relativeMapFileName);
} catch (AlreadyInListException $exception) {
$this->maniaControl->getChat()->sendException($exception, $login);
return;
}
$this->updateFullMapList();
// Update Mx MapInfo
$this->maniaControl->getMapManager()->getMXManager()->updateMapObjectsWithManiaExchangeIds(array($mapInfo));
// Update last updated time
$map = $this->getMapByUid($mapInfo->uid);
if (!$map) {
// TODO: improve this - error reports about not existing maps
$this->maniaControl->getErrorHandler()->triggerDebugNotice('Map not in List after Insert!');
$this->maniaControl->getChat()->sendError('Server Error!', $login);
return;
}
$map->lastUpdate = time();
$player = $this->maniaControl->getPlayerManager()->getPlayer($login);
if (!$update) {
// Message
$message = $player->getEscapedNickname() . ' added $<' . $mapInfo->name . '$>!';
$this->maniaControl->getChat()->sendSuccess($message);
Logger::logInfo($message, true);
// Queue requested Map
$this->maniaControl->getMapManager()->getMapQueue()->addMapToMapQueue($login, $mapInfo->uid);
} else {
$message = $player->getEscapedNickname() . ' updated $<' . $mapInfo->name . '$>!';
$this->maniaControl->getChat()->sendSuccess($message);
Logger::logInfo($message, true);
}
}
/**
* Get Map by UID
*
* @param string $uid
* @return Map
*/
public function getMapByUid($uid) {
if (isset($this->maps[$uid])) {
return $this->maps[$uid];
}
return null;
}
/**
* Updates the full Map list, needed on Init, addMap and on ShuffleMaps
*/
private function updateFullMapList() {
$tempList = array();
try {
$offset = 0;
while ($this->maniaControl->getClient()) {
$maps = $this->maniaControl->getClient()->getMapList(150, $offset);
foreach ($maps as $rpcMap) {
if (array_key_exists($rpcMap->uId, $this->maps)) {
// Map already exists, only update index
$tempList[$rpcMap->uId] = $this->maps[$rpcMap->uId];
} else {
// Insert Map Object
$map = $this->initializeMap($rpcMap);
$tempList[$map->uid] = $map;
}
}
$offset += 150;
}
} catch (IndexOutOfBoundException $e) {
}
// restore Sorted MapList
$this->maps = $tempList;
// Trigger own callback
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_MAPS_UPDATED);
// Write MapList
if ($this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_AUTOSAVE_MAPLIST)
) {
if ($this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_WRITE_OWN_MAPLIST_FILE)
) {
$serverLogin = $this->maniaControl->getServer()->login;
$matchSettingsFileName = "MatchSettings/{$serverLogin}.txt";
} else {
$matchSettingsFileName = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_MAPLIST_FILE);
}
try {
$this->maniaControl->getClient()->saveMatchSettings($matchSettingsFileName);
} catch (FileException $e) {
Logger::logError("Unable to write the playlist file, please checkout your MX-Folders File permissions!");
}
}
}
/**
* Initializes a Map
*
* @param mixed $rpcMap
* @return Map
*/
public function initializeMap($rpcMap) {
$map = new Map($rpcMap);
$this->saveMap($map);
return $map;
}
/**
* Save a Map in the Database
*
* @param Map $map
* @return bool
*/
private function saveMap(Map &$map) {
//TODO saveMaps for whole maplist at once (usage of prepared statements)
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$mapQuery = "INSERT INTO `" . self::TABLE_MAPS . "` (
`uid`,
`name`,
`authorLogin`,
`fileName`,
`environment`,
`mapType`
) VALUES (
?, ?, ?, ?, ?, ?
) ON DUPLICATE KEY UPDATE
`index` = LAST_INSERT_ID(`index`),
`fileName` = VALUES(`fileName`),
`environment` = VALUES(`environment`),
`mapType` = VALUES(`mapType`);";
$mapStatement = $mysqli->prepare($mapQuery);
if ($mysqli->error) {
trigger_error($mysqli->error);
return false;
}
$mapStatement->bind_param('ssssss', $map->uid, $map->rawName, $map->authorLogin, $map->fileName, $map->environment, $map->mapType);
$mapStatement->execute();
if ($mapStatement->error) {
trigger_error($mapStatement->error);
$mapStatement->close();
return false;
}
$map->index = $mapStatement->insert_id;
$mapStatement->close();
return true;
}
/**
* Get's a Map by it's Mania-Exchange Id
*
* @param int $mxId
* @return Map
*/
public function getMapByMxId($mxId) {
foreach ($this->maps as $map) {
if ($map->mx && $map->mx->id == $mxId) {
return $map;
}
}
return null;
}
/**
* Shuffles the MapList
*
* @param Player $admin
* @return bool
*/
public function shuffleMapList($admin = null) {
$shuffledMaps = $this->maps;
shuffle($shuffledMaps);
$mapArray = array();
foreach ($shuffledMaps as $map) {
/** @var Map $map */
$mapArray[] = $map->fileName;
}
try {
$this->maniaControl->getClient()->chooseNextMapList($mapArray);
} catch (Exception $e) {
//TODO temp added 19.04.2014
$this->maniaControl->getErrorHandler()->triggerDebugNotice("Exception line 331 MapManager" . $e->getMessage());
trigger_error("Couldn't shuffle mapList. " . $e->getMessage());
return false;
}
$this->fetchCurrentMap();
if ($admin) {
$message = $admin->getEscapedNickname() . ' shuffled the Maplist!';
$this->maniaControl->getChat()->sendSuccess($message);
Logger::logInfo($message, true);
}
// Restructure if needed
$this->restructureMapList();
return true;
}
/**
* Freshly fetch current Map
*
* @return Map
*/
private function fetchCurrentMap() {
try {
$rpcMap = $this->maniaControl->getClient()->getCurrentMapInfo();
} catch (UnavailableFeatureException $exception) {
return null;
}
if (array_key_exists($rpcMap->uId, $this->maps)) {
$this->currentMap = $this->maps[$rpcMap->uId];
$this->currentMap->nbCheckpoints = $rpcMap->nbCheckpoints;
$this->currentMap->nbLaps = $rpcMap->nbLaps;
return $this->currentMap;
}
$this->currentMap = $this->initializeMap($rpcMap);
$this->maps[$this->currentMap->uid] = $this->currentMap;
return $this->currentMap;
}
/**
* Restructures the Maplist
*/
public function restructureMapList() {
$currentIndex = $this->getMapIndex($this->getCurrentMap());
// No RestructureNeeded
if ($currentIndex < Maplist::MAX_MAPS_PER_PAGE - 1) {
return true;
}
$lowerMapArray = array();
$higherMapArray = array();
$index = 0;
foreach ($this->maps as $map) {
if ($index < $currentIndex) {
$lowerMapArray[] = $map->fileName;
} else {
$higherMapArray[] = $map->fileName;
}
$index++;
}
$mapArray = array_merge($higherMapArray, $lowerMapArray);
array_shift($mapArray);
try {
$this->maniaControl->getClient()->chooseNextMapList($mapArray);
} catch (Exception $e) {
trigger_error("Error restructuring the Maplist. " . $e->getMessage());
return false;
}
return true;
}
/**
* Returns the MapIndex of a given map
*
* @param Map $map
* @return int
*/
public function getMapIndex(Map $map) {
$maps = $this->getMaps();
return array_search($map, $maps);
}
/**
* Get all Maps
*
* @param int $offset
* @param int $length
* @return Map[]
*/
public function getMaps($offset = null, $length = null) {
if ($offset === null) {
return array_values($this->maps);
}
if ($length === null) {
return array_slice($this->maps, $offset);
}
return array_slice($this->maps, $offset, $length);
}
/**
* Get Current Map
*
* @return Map
*/
public function getCurrentMap() {
if (!$this->currentMap) {
return $this->fetchCurrentMap();
}
return $this->currentMap;
}
/**
* Handle OnInit callback
*/
public function handleOnInit() {
$this->updateFullMapList();
$this->fetchCurrentMap();
// Restructure Maplist
$this->restructureMapList();
}
/**
* Handle AfterInit callback
*/
public function handleAfterInit() {
// Fetch MX infos
$this->getMXManager()->fetchManiaExchangeMapInformation();
}
/**
* Handle Script BeginMap callback
*
* @param string $mapUid
* @param string $restart
*/
public function handleScriptBeginMap($mapUid, $restart) {
$this->beginMap($mapUid, Formatter::parseBoolean($restart));
}
/**
* Manage the Begin of a Map
*
* @param string $uid
* @param bool $restart
*/
private function beginMap($uid, $restart = false) {
//If a restart occurred, first call the endMap to set variables back
if ($restart) {
$this->endMap();
}
if ($this->mapBegan) {
return;
}
$this->mapBegan = true;
$this->mapEnded = false;
if (array_key_exists($uid, $this->maps)) {
// Map already exists, only update index
$this->currentMap = $this->maps[$uid];
if (!$this->currentMap->nbCheckpoints || !$this->currentMap->nbLaps) {
$rpcMap = $this->maniaControl->getClient()->getCurrentMapInfo();
$this->currentMap->nbLaps = $rpcMap->nbLaps;
$this->currentMap->nbCheckpoints = $rpcMap->nbCheckpoints;
}
}
// Restructure MapList if id is over 15
$this->restructureMapList();
// Update the mx of the map (for update checks, etc.)
$this->getMXManager()->fetchManiaExchangeMapInformation($this->currentMap);
// Trigger own BeginMap callback
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::BEGINMAP, $this->currentMap);
}
/**
* Manage the End of a Map
*/
private function endMap() {
if ($this->mapEnded) {
return;
}
$this->mapEnded = true;
$this->mapBegan = false;
// Trigger own EndMap callback
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::ENDMAP, $this->currentMap);
}
/**
* Fetch a map by its file path
*
* @param string $relativeFileName
* @return Map
*/
public function fetchMapByFileName($relativeFileName) {
$mapInfo = $this->maniaControl->getClient()->getMapInfo($relativeFileName);
if (!$mapInfo) {
return null;
}
return $this->initializeMap($mapInfo);
}
/**
* Handle BeginMap callback
*
* @param array $callback
*/
public function handleBeginMap(array $callback) {
$this->beginMap($callback[1][0]["UId"]);
}
/**
* Handle Script EndMap Callback
*/
public function handleScriptEndMap() {
$this->endMap();
}
/**
* Handle EndMap Callback
*
* @param array $callback
*/
public function handleEndMap(array $callback) {
$this->endMap();
}
/**
* Handle Maps Modified Callback
*
* @param array $callback
*/
public function mapsModified(array $callback) {
$this->updateFullMapList();
}
/**
* Get the Number of Maps
*
* @return int
*/
public function getMapsCount() {
return count($this->maps);
}
}

491
core/Maps/MapQueue.php Normal file
View File

@ -0,0 +1,491 @@
<?php
namespace ManiaControl\Maps;
use ManiaControl\Admin\AuthenticationManager;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\Callbacks;
use ManiaControl\Commands\CommandListener;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
use ManiaControl\Players\Player;
use ManiaControl\Utils\Formatter;
use Maniaplanet\DedicatedServer\Xmlrpc\NextMapException;
use Maniaplanet\DedicatedServer\Xmlrpc\NotInListException;
/**
* ManiaControl Map Queue Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class MapQueue implements CallbackListener, CommandListener {
/*
* Constants
*/
const CB_MAPQUEUE_CHANGED = 'MapQueue.MapQueueBoxChanged';
const SETTING_SKIP_MAP_ON_LEAVE = 'Skip Map when the requester leaves';
const SETTING_SKIP_MAPQUEUE_ADMIN = 'Skip Map when admin leaves';
const SETTING_MAPLIMIT_PLAYER = 'Maximum maps per player in the Map-Queue (-1 = unlimited)';
const SETTING_MAPLIMIT_ADMIN = 'Maximum maps per admin (Admin+) in the Map-Queue (-1 = unlimited)';
const SETTING_BUFFERSIZE = 'Size of the Map-Queue buffer (recently played maps)';
const SETTING_PERMISSION_CLEAR_MAPQUEUE = 'Clear MapQueue';
const SETTING_PERMISSION_QUEUE_BUFFER = 'Queue maps in buffer';
const ADMIN_COMMAND_CLEAR_MAPQUEUE = 'clearmapqueue';
const ADMIN_COMMAND_CLEAR_JUKEBOX = 'clearjukebox';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
private $queuedMaps = array();
private $nextMap = null;
private $buffer = array();
private $nextNoQueue = false;
/**
* Construct a new map queue instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::ENDMAP, $this, 'endMap');
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::BEGINMAP, $this, 'beginMap');
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::AFTERINIT, $this, 'handleAfterInit');
// Settings
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_SKIP_MAP_ON_LEAVE, true);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_SKIP_MAPQUEUE_ADMIN, false);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_MAPLIMIT_PLAYER, 1);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_MAPLIMIT_ADMIN, -1);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_BUFFERSIZE, 10);
// Permissions
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_CLEAR_MAPQUEUE, AuthenticationManager::AUTH_LEVEL_MODERATOR);
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_QUEUE_BUFFER, AuthenticationManager::AUTH_LEVEL_ADMIN);
// Admin Commands
$this->maniaControl->getCommandManager()->registerCommandListener(self::ADMIN_COMMAND_CLEAR_JUKEBOX, $this, 'command_ClearMapQueue', true, 'Clears the Map-Queue.');
$this->maniaControl->getCommandManager()->registerCommandListener(self::ADMIN_COMMAND_CLEAR_MAPQUEUE, $this, 'command_ClearMapQueue', true, 'Clears the Map-Queue.');
$this->maniaControl->getCommandManager()->registerCommandListener(array('jb', 'jukebox', 'mapqueue'), $this, 'command_MapQueue', false, 'Shows current maps in Map-Queue.');
}
/**
* Don't queue on the next MapChange
*/
public function dontQueueNextMapChange() {
$this->nextNoQueue = true;
}
/**
* Add current map to buffer on startup
*/
public function handleAfterInit() {
$currentMap = $this->maniaControl->getMapManager()->getCurrentMap();
$this->buffer[] = $currentMap->uid;
}
/**
* Clear the map-queue via admin command clear map queue
*
* @param array $chatCallback
* @param Player $admin
*/
public function command_ClearMapQueue(array $chatCallback, Player $admin) {
$this->clearMapQueue($admin);
}
/**
* Clear the Map Queue
*
* @param Player $admin |null
*/
public function clearMapQueue(Player $admin = null) {
if ($admin && !$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_CLEAR_MAPQUEUE)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($admin);
return;
}
if ($admin && empty($this->queuedMaps)) {
$this->maniaControl->getChat()->sendError('$fa0There are no maps in the jukebox!', $admin->login);
return;
}
//Destroy map - queue list
$this->queuedMaps = array();
if ($admin) {
$title = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($admin->authLevel);
$message = '$fa0' . $title . ' $<$fff' . $admin->nickname . '$> cleared the Map-Queue!';
$this->maniaControl->getChat()->sendInformation($message);
Logger::logInfo($message, true);
}
// Trigger callback
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_MAPQUEUE_CHANGED, array('clear'));
}
/**
* Handle the mapqueue/jukebox command
*
* @param array $chatCallback
* @param Player $player
*/
public function command_MapQueue(array $chatCallback, Player $player) {
$chatCommands = explode(' ', $chatCallback[1][2]);
if (isset($chatCommands[1])) {
$listParam = strtolower($chatCommands[1]);
switch ($listParam) {
case 'list':
$this->showMapQueue($player);
break;
case 'display':
$this->showMapQueueManialink($player);
break;
case 'clear':
$this->clearMapQueue($player);
break;
default:
$this->showMapQueue($player);
break;
}
} else {
$this->showMapQueue($player);
}
}
/**
* Show current mapqueue in the chat
*
* @param Player $player
*/
public function showMapQueue(Player $player) {
if (empty($this->queuedMaps)) {
$this->maniaControl->getChat()->sendError('$fa0There are no maps in the jukebox!', $player->login);
return;
}
$message = '$fa0Upcoming maps in the Map-Queue:';
$index = 1;
foreach ($this->queuedMaps as $queuedMap) {
$message .= ' $<$fff' . $index . '$>. [$<$fff' . Formatter::stripCodes($queuedMap[1]->name) . '$>]';
$index++;
}
$this->maniaControl->getChat()->sendInformation($message, $player);
}
/**
* Show current mapqueue in a manialink
*
* @param Player $player
*/
public function showMapQueueManialink(Player $player) {
if (empty($this->queuedMaps)) {
$this->maniaControl->getChat()->sendError('There are no Maps in the Jukebox!', $player);
return;
}
$maps = array();
foreach ($this->queuedMaps as $queuedMap) {
array_push($maps, $queuedMap[1]);
}
$this->maniaControl->getMapManager()->getMapList()->showMapList($player, $maps);
}
/**
* Return the current queue buffer
*
* @return string[]
*/
public function getQueueBuffer() {
return $this->buffer;
}
/**
* Add map as first map in queue (for /replay)
*
* @param Player $player
* @param Map $map
*/
public function addFirstMapToMapQueue(Player $player, Map $map) {
if ($map) {
if (array_key_exists($map->uid, $this->queuedMaps)) {
unset($this->queuedMaps[$map->uid]);
}
array_unshift($this->queuedMaps, array($player, $map, true));
}
}
/**
* Adds a Map to the Map-Queue from Plugins or whatever
*
* @param $uid
* @return bool
*/
public function serverAddMapToMapQueue($uid) {
$map = $this->maniaControl->getMapManager()->getMapByUid($uid);
if (!$map) {
return false;
}
$this->queuedMaps[$uid] = array(null, $map);
$this->maniaControl->getChat()->sendInformation('$fa0$<$fff' . $map->name . '$> has been added to the Map-Queue by the Server.');
// Trigger callback
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_MAPQUEUE_CHANGED, array('add', $this->queuedMaps[$uid]));
return true;
}
/**
* Add a Map to the map-queue
*
* @param string $login
* @param string $uid
*/
public function addMapToMapQueue($login, $uid) {
$player = $this->maniaControl->getPlayerManager()->getPlayer($login);
if (!$player) {
return;
}
// Check if the Player is muted
if ($player->isMuted()) {
$this->maniaControl->getChat()->sendError('Muted Players are not allowed to queue a map.', $player);
return;
}
//Check if player is allowed to add (another) map
$isModerator = $this->maniaControl->getAuthenticationManager()->checkRight($player, AuthenticationManager::AUTH_LEVEL_MODERATOR);
$mapsForPlayer = 0;
foreach ($this->queuedMaps as $queuedMap) {
if ($queuedMap[0]->login == $login) {
$mapsForPlayer++;
}
}
if ($isModerator) {
$maxAdmin = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_MAPLIMIT_ADMIN);
if ($maxAdmin >= 0 && $mapsForPlayer >= $maxAdmin) {
$this->maniaControl->getChat()->sendError('You already have $<$fff' . $maxAdmin . '$> map(s) in the Map-Queue!', $login);
return;
}
} else {
$maxPlayer = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_MAPLIMIT_PLAYER);
if ($maxPlayer >= 0 && $mapsForPlayer >= $maxPlayer) {
$this->maniaControl->getChat()->sendError('You already have $<$fff' . $maxPlayer . '$> map(s) in the Map-Queue!', $login);
return;
}
}
// Check if the map is already juked
$map = null;
if ($uid instanceof Map) {
$map = $uid;
$uid = $map->uid;
}
if (array_key_exists($uid, $this->queuedMaps)) {
$this->maniaControl->getChat()->sendError('That map is already in the Map-Queue!', $login);
return;
}
//TODO recently maps not able to add to queue-amps setting, and management
// Check if map is in the buffer
if (in_array($uid, $this->buffer)) {
$this->maniaControl->getChat()->sendError('That map has recently been played!', $login);
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CLEAR_MAPQUEUE)
) {
return;
}
}
if (!$map) {
$map = $this->maniaControl->getMapManager()->getMapByUid($uid);
}
$this->queuedMaps[$uid] = array($player, $map);
$this->maniaControl->getChat()->sendInformation('$fa0$<$fff' . $map->name . '$> has been added to the Map-Queue by $<$fff' . $player->nickname . '$>.');
// Trigger callback
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_MAPQUEUE_CHANGED, array('add', $this->queuedMaps[$uid]));
}
/**
* Remove a Map from the Map queue
*
* @param Player $player
* @param string $uid
*/
public function removeFromMapQueue(Player $player, $uid) {
if (!isset($this->queuedMaps[$uid])) {
return;
}
/** @var Map $map */
$map = $this->queuedMaps[$uid][1];
unset($this->queuedMaps[$uid]);
$this->maniaControl->getChat()->sendInformation('$fa0$<$fff' . $map->name . '$> is removed from the Map-Queue by $<$fff' . $player->nickname . '$>.');
// Trigger callback
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_MAPQUEUE_CHANGED, array('remove', $map));
}
/**
* Called on endmap
*
* @param Map $map
*/
public function endMap(Map $map = null) {
//Don't queue next map (for example on skip to map)
if ($this->nextNoQueue) {
$this->nextNoQueue = false;
return;
}
$this->nextMap = null;
if ($this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_SKIP_MAP_ON_LEAVE)
) {
// Skip Map if requester has left
foreach ($this->queuedMaps as $queuedMap) {
$player = $queuedMap[0];
// Check if map is added via replay vote/command
if (isset($queuedMap[2]) && $queuedMap[2] === true) {
break;
}
// Player found, so play this map (or if it got juked by the server)
if ($player == null || $this->maniaControl->getPlayerManager()->getPlayer($player->login)) {
break;
}
if (!$this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_SKIP_MAPQUEUE_ADMIN)) {
//Check if the queuer is a admin
if ($player->authLevel > 0) {
break;
}
}
// Trigger callback
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_MAPQUEUE_CHANGED, array('skip', $queuedMap[0]));
// Player not found, so remove the map from the mapqueue
array_shift($this->queuedMaps);
$this->maniaControl->getChat()->sendInformation('$fa0$<$fff' . $queuedMap[0]->name . '$> is skipped because $<' . $player->nickname . '$> left the game!');
}
}
$this->nextMap = array_shift($this->queuedMaps);
//Check if Map Queue is empty
if (!$this->nextMap || !isset($this->nextMap[1])) {
return;
}
$map = $this->nextMap[1];
//Message only if it's juked by a player (not by the server)
if ($this->nextMap[0]) {
/** @var Map $map */
$this->maniaControl->getChat()->sendInformation('$fa0Next map will be $<$fff' . $map->name . '$> as requested by $<' . $this->nextMap[0]->nickname . '$>.');
}
try {
$this->maniaControl->getClient()->setNextMapIdent($map->uid);
} catch (NextMapException $e) {
} catch (NotInListException $e) {
}
}
/**
* Called on begin map
*
* @param Map $map
*/
public function beginMap(Map $map) {
if (in_array($map->uid, $this->buffer)) {
return;
}
if (count($this->buffer) >= $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_BUFFERSIZE)
) {
array_shift($this->buffer);
}
$this->buffer[] = $map->uid;
}
/**
* Return the next Map if the next map is a queuedmap or null if it's not
*
* @return Map
*/
public function getNextMap() {
return $this->nextMap;
}
/**
* Return the first Queued Map
*
* @return array(Player $player, Map $map)
*/
public function getNextQueuedMap() {
foreach ($this->queuedMaps as $queuedMap) {
//return the first Queued Map
return $queuedMap;
}
return null;
}
/**
* Return a list with the indexes of the queued maps
*
* @return array
*/
public function getQueuedMapsRanking() {
$index = 1;
$queuedMaps = array();
foreach ($this->queuedMaps as $queuedMap) {
$map = $queuedMap[1];
$queuedMaps[$map->uid] = $index;
$index++;
}
return $queuedMaps;
}
/**
* Return the Queuer of a Map
*
* @param string $uid
* @return mixed
*/
public function getQueuer($uid) {
return $this->queuedMaps[$uid][0];
}
/**
* Dummy Function for testing
*/
public function printAllMaps() {
foreach ($this->queuedMaps as $map) {
$map = $map[1];
var_dump($map->name);
}
}
}

360
core/Players/Player.php Normal file
View File

@ -0,0 +1,360 @@
<?php
namespace ManiaControl\Players;
use ManiaControl\ManiaControl;
use ManiaControl\Utils\ClassUtil;
use ManiaControl\Utils\Formatter;
/**
* Player Model Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class Player {
/*
* Public Properties
*/
public $index = -1;
public $pid = -1;
public $login = null;
public $nickname = null;
public $rawNickname = null;
public $path = null;
public $authLevel = 0;
public $language = null;
public $avatar = null;
public $allies = array();
public $clubLink = null;
public $teamId = -1;
public $isOfficial = null;
public $ladderScore = -1.;
public $ladderRank = -1;
public $ladderStats = null;
public $joinTime = -1;
public $ipAddress = null;
public $isConnected = true;
public $clientVersion = null;
public $downloadRate = -1;
public $uploadRate = -1;
public $skins = null;
public $daysSinceZoneInscription = -1;
//Flags details
public $forcedSpectatorState = 0;
public $isReferee = false;
public $isPodiumReady = false;
public $isUsingStereoscopy = false;
public $isManagedByAnOtherServer = false;
public $isServer = false;
public $hasPlayerSlot = false;
public $isBroadcasting = false;
public $hasJoinedGame = false;
//SpectatorStatus details
public $isSpectator = false;
public $isTemporarySpectator = false;
public $isPureSpectator = false;
public $autoTarget = false;
public $currentTargetId = 0;
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
private $cache = array();
/**
* Construct a new Player
*
* @param ManiaControl $maniaControl
* @param bool $connected
*/
public function __construct(ManiaControl $maniaControl, $connected) {
$this->maniaControl = $maniaControl;
$this->isConnected = (bool)$connected;
if ($connected) {
$this->joinTime = time();
}
}
/**
* Get the Login of the Player
*
* @param mixed $player
* @return string
*/
public static function parseLogin($player) {
if (is_object($player) && property_exists($player, 'login')) {
return (string)$player->login;
}
return (string)$player;
}
/**
* Get the Escaped Nickname
*
* @return string
*/
public function getEscapedNickname() {
$nickname = $this->nickname;
if (!$nickname) {
$nickname = $this->login;
}
return Formatter::escapeText($nickname);
}
/**
* Update from ManiaPlanet PlayerInfo structure
*
* @param \Maniaplanet\DedicatedServer\Structures\PlayerInfo $mpPlayer
*/
public function setInfo($mpPlayer) {
$this->pid = $mpPlayer->playerId;
$this->login = $mpPlayer->login;
$this->nickname = Formatter::stripDirtyCodes($mpPlayer->nickName);
$this->rawNickname = $mpPlayer->nickName;
$this->teamId = $mpPlayer->teamId;
$this->isOfficial = $mpPlayer->isInOfficialMode;
//Flag Details
$this->forcedSpectatorState = $mpPlayer->forceSpectator;
$this->isReferee = $mpPlayer->isReferee;
$this->isPodiumReady = $mpPlayer->isPodiumReady;
$this->isUsingStereoscopy = $mpPlayer->isUsingStereoscopy;
$this->isServer = $mpPlayer->isServer;
$this->isManagedByAnOtherServer = $mpPlayer->isManagedByAnOtherServer;
$this->hasPlayerSlot = $mpPlayer->hasPlayerSlot;
$this->hasJoinedGame = $mpPlayer->hasJoinedGame;
$this->isBroadcasting = $mpPlayer->isBroadcasting;
//Spectator Status
$this->isSpectator = $mpPlayer->spectator;
$this->isTemporarySpectator = $mpPlayer->temporarySpectator;
$this->isPureSpectator = $mpPlayer->pureSpectator;
$this->autoTarget = $mpPlayer->autoTarget;
$this->currentTargetId = $mpPlayer->currentTargetId;
if (!$this->nickname) {
$this->nickname = $this->login;
}
}
/**
* Update from ManiaPlanet PlayerDetailedInfo structure
*
* @param \Maniaplanet\DedicatedServer\Structures\PlayerDetailedInfo $mpPlayer
*/
public function setDetailedInfo($mpPlayer) {
$this->pid = $mpPlayer->playerId;
$this->login = $mpPlayer->login;
$this->nickname = Formatter::stripDirtyCodes($mpPlayer->nickName);
$this->rawNickname = $mpPlayer->nickName;
$this->path = $mpPlayer->path;
$this->language = $mpPlayer->language;
$this->avatar = $mpPlayer->avatar->fileName;
$this->allies = $mpPlayer->allies;
$this->clubLink = $mpPlayer->clubLink;
$this->teamId = $mpPlayer->teamId;
$this->isOfficial = $mpPlayer->isInOfficialMode;
$this->ladderScore = $mpPlayer->ladderStats->playerRankings[0]->score;
$this->ladderRank = $mpPlayer->ladderStats->playerRankings[0]->ranking;
$this->ladderStats = $mpPlayer->ladderStats;
$this->daysSinceZoneInscription = $mpPlayer->hoursSinceZoneInscription / 24;
$this->ipAddress = $mpPlayer->iPAddress;
$this->clientVersion = $mpPlayer->clientVersion;
$this->downloadRate = $mpPlayer->downloadRate;
$this->uploadRate = $mpPlayer->uploadRate;
$this->skins = $mpPlayer->skins;
if (!$this->nickname) {
$this->nickname = $this->login;
}
}
/**
* Check if player is not a real player
*
* @return bool
*/
public function isFakePlayer() {
return ($this->pid <= 0 || substr($this->login, 0, 1) === '*');
}
/**
* Get province
*
* @return string
*/
public function getProvince() {
return $this->getPathPart(3);
}
/**
* Get the specified Part of the Path
*
* @param int $partNumber
* @return string
*/
public function getPathPart($partNumber) {
$pathParts = explode('|', $this->path);
for ($partIndex = $partNumber; $partIndex >= 0; $partIndex--) {
if (isset($pathParts[$partIndex])) {
return $pathParts[$partIndex];
}
}
return $this->path;
}
/**
* Get Country
*
* @return string
*/
public function getCountry() {
return $this->getPathPart(2);
}
/**
* Get Continent
*
* @return string
*/
public function getContinent() {
return $this->getPathPart(1);
}
/**
* Update the Flags of the Player
*
* @param $flags
*/
public function updatePlayerFlags($flags) {
//Detail flags
$this->forcedSpectatorState = $flags % 10; // 0, 1 or 2
$this->isReferee = (bool)(intval($flags / 10) % 10);
$this->isPodiumReady = (bool)(intval($flags / 100) % 10);
$this->isUsingStereoscopy = (bool)(intval($flags / 1000) % 10);
$this->isManagedByAnOtherServer = (bool)(intval($flags / 10000) % 10);
$this->isServer = (bool)(intval($flags / 100000) % 10);
$this->hasPlayerSlot = (bool)(intval($flags / 1000000) % 10);
$this->isBroadcasting = (bool)(intval($flags / 10000000) % 10);
$this->hasJoinedGame = (bool)(intval($flags / 100000000) % 10);
}
/**
* Update the Spectator Status of the player
*
* @param $spectatorStatus
*/
public function updateSpectatorStatus($spectatorStatus) {
//Details spectatorStatus
$this->isSpectator = (bool)($spectatorStatus % 10);
$this->isTemporarySpectator = (bool)(intval($spectatorStatus / 10) % 10);
$this->isPureSpectator = (bool)(intval($spectatorStatus / 100) % 10);
$this->autoTarget = (bool)(intval($spectatorStatus / 1000) % 10);
$this->currentTargetId = intval($spectatorStatus / 10000);
}
/**
* Get the Cache with the given Name
*
* @param $object
* @param string $cacheName
* @return mixed
*/
public function getCache($object, $cacheName) {
$className = ClassUtil::getClass($object);
if (isset($this->cache[$className . $cacheName])) {
return $this->cache[$className . $cacheName];
}
return null;
}
/**
* Set the Cache Data for the given Name
*
* @param mixed $object
* @param string $cacheName
* @param mixed $data
*/
public function setCache($object, $cacheName, $data) {
$className = ClassUtil::getClass($object);
$this->cache[$className . $cacheName] = $data;
}
/**
* Destroy a Cache
*
* @param mixed $object
* @param string $cacheName
*/
public function destroyCache($object, $cacheName) {
$className = ClassUtil::getClass($object);
unset($this->cache[$className . $cacheName]);
}
/**
* Clear the Player's Temporary Data
*/
public function clearCache() {
$this->cache = array();
}
/**
* Gets the Player Data
*
* @param mixed $object
* @param string $dataName
* @param int $serverIndex
* @return mixed
*/
public function getPlayerData($object, $dataName, $serverIndex = -1) {
return $this->maniaControl->getPlayerManager()->getPlayerDataManager()->getPlayerData($object, $dataName, $this, $serverIndex);
}
/**
* Sets the Player Data and stores it in the Database
*
* @param mixed $object
* @param string $dataName
* @param mixed $value
* @param int $serverIndex
* @return bool
*/
public function setPlayerData($object, $dataName, $value, $serverIndex = -1) {
return $this->maniaControl->getPlayerManager()->getPlayerDataManager()->setPlayerData($object, $dataName, $this, $value, $serverIndex);
}
/*
* Check if a Player is muted
*
* @return bool
*/
public function isMuted() {
$ignoreList = $this->maniaControl->getClient()->getIgnoreList(100, 0);
foreach ($ignoreList as $ignoredPlayers) {
if ($ignoredPlayers->login === $this->login) {
return true;
}
}
return false;
}
/**
* Var_Dump the Player
*/
public function dump() {
var_dump(json_decode(json_encode($this)));
}
/**
* Var_Dump the Players Cache
*/
public function dumpCache() {
var_dump($this->cache);
}
}

View File

@ -0,0 +1,523 @@
<?php
namespace ManiaControl\Players;
use FML\Controls\Frame;
use FML\Controls\Labels\Label_Text;
use FML\Controls\Quad;
use FML\Controls\Quads\Quad_Icons64x64_1;
use FML\ManiaLink;
use ManiaControl\Admin\AuthenticationManager;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
use ManiaControl\Manialinks\ManialinkManager;
use Maniaplanet\DedicatedServer\Xmlrpc\AlreadyInListException;
use Maniaplanet\DedicatedServer\Xmlrpc\FaultException;
use Maniaplanet\DedicatedServer\Xmlrpc\GameModeException;
use Maniaplanet\DedicatedServer\Xmlrpc\NotInListException;
use Maniaplanet\DedicatedServer\Xmlrpc\PlayerStateException;
use Maniaplanet\DedicatedServer\Xmlrpc\ServerOptionsException;
use Maniaplanet\DedicatedServer\Xmlrpc\UnknownPlayerException;
/**
* Player Actions Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class PlayerActions {
/*
* Constants
*/
const TEAM_BLUE = 0;
const TEAM_RED = 1;
const SPECTATOR_USER_SELECTABLE = 0;
const SPECTATOR_SPECTATOR = 1;
const SPECTATOR_PLAYER = 2;
const SPECTATOR_BUT_KEEP_SELECTABLE = 3;
/*
* Permission Setting Constants
*/
const SETTING_PERMISSION_FORCE_PLAYER_PLAY = 'Force Player to Play';
const SETTING_PERMISSION_FORCE_PLAYER_TEAM = 'Force Player to Team';
const SETTING_PERMISSION_FORCE_PLAYER_SPEC = 'Force Player to Spec';
const SETTING_PERMISSION_MUTE_PLAYER = 'Mute Player';
const SETTING_PERMISSION_WARN_PLAYER = 'Warn Player';
const SETTING_PERMISSION_KICK_PLAYER = 'Kick Player';
const SETTING_PERMISSION_BAN_PLAYER = 'Ban Player';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/**
* Construct a new PlayerActions instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Permissions
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_BAN_PLAYER, AuthenticationManager::AUTH_LEVEL_ADMIN);
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_KICK_PLAYER, AuthenticationManager::AUTH_LEVEL_MODERATOR);
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_WARN_PLAYER, AuthenticationManager::AUTH_LEVEL_MODERATOR);
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_MUTE_PLAYER, AuthenticationManager::AUTH_LEVEL_MODERATOR);
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_FORCE_PLAYER_PLAY, AuthenticationManager::AUTH_LEVEL_MODERATOR);
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_FORCE_PLAYER_TEAM, AuthenticationManager::AUTH_LEVEL_MODERATOR);
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_FORCE_PLAYER_SPEC, AuthenticationManager::AUTH_LEVEL_MODERATOR);
}
/**
* Force a Player to a certain Team
*
* @param string $adminLogin
* @param string $targetLogin
* @param int $teamId
*/
public function forcePlayerToTeam($adminLogin, $targetLogin, $teamId) {
$admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_FORCE_PLAYER_TEAM)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($admin);
return;
}
$target = $this->maniaControl->getPlayerManager()->getPlayer($targetLogin);
if (!$target || !$admin) {
return;
}
if ($target->isSpectator) {
try {
if (!$this->forcePlayerToPlay($adminLogin, $targetLogin, true, false)) {
return;
}
} catch (FaultException $exception) {
$this->maniaControl->getChat()->sendException($exception, $admin);
}
}
try {
$this->maniaControl->getClient()->forcePlayerTeam($target->login, $teamId);
} catch (ServerOptionsException $exception) {
$this->forcePlayerToPlay($adminLogin, $targetLogin);
return;
} catch (GameModeException $exception) {
$this->maniaControl->getChat()->sendException($exception, $admin);
return;
}
$chatMessage = false;
$title = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($admin->authLevel);
if ($teamId === self::TEAM_BLUE) {
$chatMessage = $title . ' ' . $admin->getEscapedNickname() . ' forced ' . $target->getEscapedNickname() . ' into the Blue-Team!';
} else if ($teamId === self::TEAM_RED) {
$chatMessage = $title . ' ' . $admin->getEscapedNickname() . ' forced ' . $target->getEscapedNickname() . ' into the Red-Team!';
}
if (!$chatMessage) {
return;
}
$this->maniaControl->getChat()->sendInformation($chatMessage);
Logger::logInfo($chatMessage, true);
}
/**
* Force a Player to Play
*
* @param string $adminLogin
* @param string $targetLogin
* @param bool $userIsAbleToSelect
* @param bool $displayAnnouncement
* @return bool
*/
public function forcePlayerToPlay($adminLogin, $targetLogin, $userIsAbleToSelect = true, $displayAnnouncement = true) {
$admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_FORCE_PLAYER_PLAY)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($admin);
return false;
}
$target = $this->maniaControl->getPlayerManager()->getPlayer($targetLogin);
if (!$target) {
return false;
}
try {
$this->maniaControl->getClient()->forceSpectator($target->login, self::SPECTATOR_PLAYER);
} catch (ServerOptionsException $exception) {
$this->maniaControl->getChat()->sendException($exception, $admin);
return false;
}
if ($userIsAbleToSelect) {
try {
$this->maniaControl->getClient()->forceSpectator($target->login, self::SPECTATOR_USER_SELECTABLE);
} catch (ServerOptionsException $exception) {
$this->maniaControl->getChat()->sendException($exception, $admin);
return false;
}
}
// Announce force
if ($displayAnnouncement) {
$chatMessage = $admin->getEscapedNickname() . ' forced ' . $target->getEscapedNickname() . ' to Play!';
$this->maniaControl->getChat()->sendInformation($chatMessage);
}
return true;
}
/**
* Force a Player to Spectator
*
* @param string $adminLogin
* @param string $targetLogin
* @param int $spectatorState
* @param bool $releaseSlot
*/
public function forcePlayerToSpectator($adminLogin, $targetLogin, $spectatorState = self::SPECTATOR_BUT_KEEP_SELECTABLE, $releaseSlot = true) {
$admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_FORCE_PLAYER_SPEC)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($admin);
return;
}
$target = $this->maniaControl->getPlayerManager()->getPlayer($targetLogin);
if (!$admin || !$target || $target->isSpectator) {
return;
}
try {
$this->maniaControl->getClient()->forceSpectator($target->login, $spectatorState);
} catch (ServerOptionsException $exception) {
$this->maniaControl->getChat()->sendException($exception, $admin->login);
return;
}
$title = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($admin->authLevel);
$chatMessage = $title . ' ' . $admin->getEscapedNickname() . ' forced ' . $target->getEscapedNickname() . ' to Spectator!';
$this->maniaControl->getChat()->sendInformation($chatMessage);
Logger::logInfo($chatMessage, true);
if ($releaseSlot) {
// Free player slot
try {
$this->maniaControl->getClient()->spectatorReleasePlayerSlot($target->login);
} catch (PlayerStateException $e) {
} catch (UnknownPlayerException $e) {
}
}
}
/**
* UnMute a Player
*
* @param string $adminLogin
* @param string $targetLogin
*/
public function unMutePlayer($adminLogin, $targetLogin) {
$admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_MUTE_PLAYER)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($admin);
return;
}
$target = $this->maniaControl->getPlayerManager()->getPlayer($targetLogin);
if (!$target) {
return;
}
try {
$this->maniaControl->getClient()->unIgnore($targetLogin);
} catch (NotInListException $e) {
$this->maniaControl->getChat()->sendError('Player is not ignored!', $adminLogin);
return;
}
$title = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($admin->authLevel);
$chatMessage = $title . ' ' . $admin->getEscapedNickname() . ' un-muted ' . $target->getEscapedNickname() . '!';
$this->maniaControl->getChat()->sendInformation($chatMessage);
Logger::logInfo($chatMessage, true);
}
/**
* Mute a Player
*
* @param string $adminLogin
* @param string $targetLogin
*/
public function mutePlayer($adminLogin, $targetLogin) {
$admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_MUTE_PLAYER)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($admin);
return;
}
$target = $this->maniaControl->getPlayerManager()->getPlayer($targetLogin);
if (!$target) {
return;
}
try {
$this->maniaControl->getClient()->ignore($targetLogin);
} catch (AlreadyInListException $e) {
$this->maniaControl->getChat()->sendError("Player already ignored!", $adminLogin);
return;
}
$title = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($admin->authLevel);
$chatMessage = $title . ' ' . $admin->getEscapedNickname() . ' muted ' . $target->getEscapedNickname() . '!';
$this->maniaControl->getChat()->sendInformation($chatMessage);
Logger::logInfo($chatMessage, true);
}
/**
* Warn a Player
*
* @param string $adminLogin
* @param string $targetLogin
*/
public function warnPlayer($adminLogin, $targetLogin) {
$admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_WARN_PLAYER)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($admin);
return;
}
$target = $this->maniaControl->getPlayerManager()->getPlayer($targetLogin);
if (!$target) {
return;
}
// Display warning message
$message = '$s$f00This is an administrative warning.{br}{br}$gWhatever you wrote or you have done is against {br} our server\'s policy.
{br}Not respecting other players, or{br}using offensive language might result in a{br}$f00kick, or ban $ff0the next time.
{br}{br}$gThe server administrators.';
$message = preg_split('/{br}/', $message);
// Build Manialink
$width = 80;
$height = 50;
$quadStyle = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultMainWindowStyle();
$quadSubstyle = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultMainWindowSubStyle();
$maniaLink = new ManiaLink(ManialinkManager::MAIN_MLID);
$frame = new Frame();
$maniaLink->add($frame);
$frame->setPosition(0, 10);
// Background
$backgroundQuad = new Quad();
$frame->add($backgroundQuad);
$backgroundQuad->setSize($width, $height);
$backgroundQuad->setStyles($quadStyle, $quadSubstyle);
// Close Quad (X)
$closeQuad = new Quad_Icons64x64_1();
$frame->add($closeQuad);
$closeQuad->setPosition($width * 0.473, $height * 0.457, 3);
$closeQuad->setSize(6, 6);
$closeQuad->setSubStyle($closeQuad::SUBSTYLE_QuitRace);
$closeQuad->setAction(ManialinkManager::ACTION_CLOSEWIDGET);
// Headline
$label = new Label_Text();
$frame->add($label);
$label->setY($height / 2 - 5);
$label->setStyle($label::STYLE_TextCardMedium);
$label->setTextSize(4);
$label->setText('Administrative Warning');
$label->setTextColor('f00');
$posY = $height / 2 - 15;
foreach ($message as $line) {
// Message lines
$label = new Label_Text();
$frame->add($label);
$label->setY($posY);
$label->setStyle($label::STYLE_TextCardMedium);
$label->setText($line);
$label->setTextColor('ff0');
$label->setTextSize(1.3);
$posY -= 4;
}
// Display manialink
$this->maniaControl->getManialinkManager()->displayWidget($maniaLink, $target);
// Announce warning
$title = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($admin->authLevel);
$chatMessage = $title . ' ' . $admin->getEscapedNickname() . ' warned ' . $target->getEscapedNickname() . '!';
$this->maniaControl->getChat()->sendInformation($chatMessage);
Logger::log($chatMessage, true);
}
/**
* Kick a Player
*
* @param string $adminLogin
* @param string $targetLogin
* @param string $message
*/
public function kickPlayer($adminLogin, $targetLogin, $message = '') {
$admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_KICK_PLAYER)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($admin);
return;
}
$target = $this->maniaControl->getPlayerManager()->getPlayer($targetLogin);
if (!$target) {
return;
}
try {
if ($target->isFakePlayer()) {
$this->maniaControl->getClient()->disconnectFakePlayer($target->login);
} else {
$this->maniaControl->getClient()->kick($target->login, $message);
}
} catch (UnknownPlayerException $e) {
$this->maniaControl->getChat()->sendException($e, $admin);
return;
}
// Announce kick
$title = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($admin->authLevel);
$chatMessage = $title . ' ' . $admin->getEscapedNickname() . ' kicked ' . $target->getEscapedNickname() . '!';
$this->maniaControl->getChat()->sendInformation($chatMessage);
Logger::logInfo($chatMessage, true);
}
/**
* Ban a Player
*
* @param string $adminLogin
* @param string $targetLogin
* @param string $message
*/
public function banPlayer($adminLogin, $targetLogin, $message = '') {
$admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_BAN_PLAYER)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($admin);
return;
}
$target = $this->maniaControl->getPlayerManager()->getPlayer($targetLogin);
if (!$target) {
return;
}
if ($target->isFakePlayer()) {
$this->maniaControl->getChat()->sendError('It is not possible to Ban a bot', $admin);
return;
}
$this->maniaControl->getClient()->ban($target->login, $message);
// Announce ban
$title = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($admin->authLevel);
$chatMessage = $title . ' ' . $admin->getEscapedNickname() . ' banned ' . $target->getEscapedNickname() . '!';
$this->maniaControl->getChat()->sendInformation($chatMessage);
Logger::logInfo($chatMessage, true);
}
/**
* Grands the Player an Authorization Level
*
* @param string $adminLogin
* @param string $targetLogin
* @param int $authLevel
*/
public function grandAuthLevel($adminLogin, $targetLogin, $authLevel) {
$admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
$target = $this->maniaControl->getPlayerManager()->getPlayer($targetLogin);
if (!$admin || !$target) {
return;
}
$authLevelName = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($authLevel);
if (!$this->maniaControl->getAuthenticationManager()->checkRight($admin, $authLevel + 1)
) {
$this->maniaControl->getChat()->sendError("You don't have the permission to add a {$authLevelName}!", $admin);
return;
}
if ($this->maniaControl->getAuthenticationManager()->checkRight($target, $authLevel)
) {
$this->maniaControl->getChat()->sendError("This Player is already {$authLevelName}!", $admin);
return;
}
$success = $this->maniaControl->getAuthenticationManager()->grantAuthLevel($target, $authLevel);
if (!$success) {
$this->maniaControl->getChat()->sendError('Error occurred.', $admin);
return;
}
// Announce granting
$title = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($admin->authLevel);
$chatMessage = $title . ' ' . $admin->getEscapedNickname() . ' added ' . $target->getEscapedNickname() . ' as $< ' . $authLevelName . '$>!';
$this->maniaControl->getChat()->sendInformation($chatMessage);
Logger::logInfo($chatMessage, true);
}
/**
* Revokes all Rights from the Player
*
* @param string $adminLogin
* @param string $targetLogin
*/
public function revokeAuthLevel($adminLogin, $targetLogin) {
$admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
$target = $this->maniaControl->getPlayerManager()->getPlayer($targetLogin);
if (!$admin || !$target) {
return;
}
if (!$this->maniaControl->getAuthenticationManager()->checkRight($admin, $target->authLevel + 1)
) {
$title = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($target->authLevel);
$this->maniaControl->getChat()->sendError("You can't revoke the Rights of a {$title}!", $admin);
return;
}
if ($this->maniaControl->getAuthenticationManager()->checkRight($target, AuthenticationManager::AUTH_LEVEL_MASTERADMIN)
) {
$this->maniaControl->getChat()->sendError("MasterAdmins can't be removed!", $admin);
return;
}
$success = $this->maniaControl->getAuthenticationManager()->grantAuthLevel($target, AuthenticationManager::AUTH_LEVEL_PLAYER);
if (!$success) {
$this->maniaControl->getChat()->sendError('Error occurred.', $admin);
return;
}
// Announce revoke
$title = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($admin->authLevel);
$chatMessage = $title . ' ' . $admin->getEscapedNickname() . ' revoked the Rights of ' . $target->getEscapedNickname() . '!';
$this->maniaControl->getChat()->sendInformation($chatMessage);
Logger::logInfo($chatMessage, true);
}
/**
* Check if a Player is muted
*
* @deprecated see Player/isMuted()
*/
public function isPlayerMuted($login) {
return $this->maniaControl->getPlayerManager()->getPlayer($login)->isMuted();
}
}

View File

@ -0,0 +1,368 @@
<?php
namespace ManiaControl\Players;
use FML\Controls\Quads\Quad_Icons128x32_1;
use FML\Controls\Quads\Quad_UIConstruction_Buttons;
use ManiaControl\Admin\AuthenticationManager;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Commands\CommandListener;
use ManiaControl\ManiaControl;
use ManiaControl\Manialinks\ManialinkPageAnswerListener;
use ManiaControl\Server\Server;
use Maniaplanet\DedicatedServer\Xmlrpc\GameModeException;
use Maniaplanet\DedicatedServer\Xmlrpc\UnavailableFeatureException;
/**
* Class offering various Admin Commands related to Players
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class PlayerCommands implements CommandListener, ManialinkPageAnswerListener, CallbackListener {
/*
* Constants
*/
const ACTION_BALANCE_TEAMS = 'PlayerCommands.BalanceTeams';
const ACTION_OPEN_PLAYERLIST = 'PlayerCommands.OpenPlayerList';
const SETTING_PERMISSION_ADD_BOT = 'Add Bot';
const SETTING_PERMISSION_TEAM_BALANCE = 'Balance Teams';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/**
* Create a new Player Commands Instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Admin commands
$this->maniaControl->getCommandManager()->registerCommandListener(array('balance', 'teambalance', 'autoteambalance'), $this, 'command_TeamBalance', true, 'Balances the teams.');
$this->maniaControl->getCommandManager()->registerCommandListener('kick', $this, 'command_Kick', true, 'Kicks player from the server.');
$this->maniaControl->getCommandManager()->registerCommandListener('ban', $this, 'command_Ban', true, 'Bans player from the server.');
$this->maniaControl->getCommandManager()->registerCommandListener(array('forcespec', 'forcespectator'), $this, 'command_ForceSpectator', true, 'Forces player into spectator.');
$this->maniaControl->getCommandManager()->registerCommandListener('forceplay', $this, 'command_ForcePlay', true, 'Forces player into Play mode.');
$this->maniaControl->getCommandManager()->registerCommandListener('forceblue', $this, 'command_ForceBlue', true, 'Forces player into blue team.');
$this->maniaControl->getCommandManager()->registerCommandListener('forcered', $this, 'command_ForceRed', true, 'Forces player into red team.');
$this->maniaControl->getCommandManager()->registerCommandListener(array('addbots', 'addbot'), $this, 'command_AddFakePlayers', true, 'Adds bots to the game.');
$this->maniaControl->getCommandManager()->registerCommandListener(array('removebot', 'removebots'), $this, 'command_RemoveFakePlayers', true, 'Removes bots from the game.');
$this->maniaControl->getCommandManager()->registerCommandListener('mute', $this, 'command_MutePlayer', true, 'Mutes a player (prevents player from chatting).');
$this->maniaControl->getCommandManager()->registerCommandListener('unmute', $this, 'command_UnmutePlayer', true, 'Unmute a player (enables player to chat again).');
// Player commands
$this->maniaControl->getCommandManager()->registerCommandListener(array('player', 'players'), $this, 'command_playerList', false, 'Shows players currently on the server.');
// Permissions
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_ADD_BOT, AuthenticationManager::AUTH_LEVEL_MODERATOR);
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_TEAM_BALANCE, AuthenticationManager::AUTH_LEVEL_MODERATOR);
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(Server::CB_TEAM_MODE_CHANGED, $this, 'teamStatusChanged');
// Action Open PlayerList
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ACTION_OPEN_PLAYERLIST, $this, 'command_playerList');
$itemQuad = new Quad_UIConstruction_Buttons();
$itemQuad->setSubStyle($itemQuad::SUBSTYLE_Author);
$itemQuad->setAction(self::ACTION_OPEN_PLAYERLIST);
$this->maniaControl->getActionsMenu()->addMenuItem($itemQuad, true, 15, 'Open PlayerList');
}
/**
* Handle TeamStatusChanged
*
* @param bool $teamMode
*/
public function teamStatusChanged($teamMode) {
//Add Balance Team Icon if it's a teamMode
if ($teamMode) {
// Action Balance Teams
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ACTION_BALANCE_TEAMS, $this, 'command_TeamBalance');
$itemQuad = new Quad_Icons128x32_1();
$itemQuad->setSubStyle($itemQuad::SUBSTYLE_RT_Team);
$itemQuad->setAction(self::ACTION_BALANCE_TEAMS);
$this->maniaControl->getActionsMenu()->addMenuItem($itemQuad, false, 40, 'Balance Teams');
}
}
/**
* Handle //teambalance command
*
* @param array $chatCallback
* @param Player $player
*/
public function command_TeamBalance(array $chatCallback, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_TEAM_BALANCE)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
try {
$this->maniaControl->getClient()->autoTeamBalance();
} catch (GameModeException $exception) {
$this->maniaControl->getChat()->sendException($exception, $player);
return;
}
$this->maniaControl->getChat()->sendInformation($player->getEscapedNickname() . ' balanced Teams!');
}
/**
* Handle //kick command
*
* @param array $chat
* @param Player $player
*/
public function command_Kick(array $chat, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, PlayerActions::SETTING_PERMISSION_KICK_PLAYER)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$params = explode(' ', $chat[1][2], 3);
if (count($params) <= 1) {
$this->maniaControl->getChat()->sendUsageInfo("No Login given! Example: '//kick login'", $player->login);
return;
}
$targetLogin = $params[1];
$message = '';
if (isset($params[2])) {
$message = $params[2];
}
$this->maniaControl->getPlayerManager()->getPlayerActions()->kickPlayer($player->login, $targetLogin, $message);
}
/**
* Handle //ban command
*
* @param array $chat
* @param Player $player
*/
public function command_Ban(array $chat, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, PlayerActions::SETTING_PERMISSION_BAN_PLAYER)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$params = explode(' ', $chat[1][2], 3);
if (count($params) <= 1) {
$this->maniaControl->getChat()->sendUsageInfo("No Login given! Example: '//ban login'", $player->login);
return;
}
$targetLogin = $params[1];
$message = '';
if (isset($params[2])) {
$message = $params[2];
}
$this->maniaControl->getPlayerManager()->getPlayerActions()->banPlayer($player->login, $targetLogin, $message);
}
/**
* Handle //warn Command
*
* @param array $chatCallback
* @param Player $player
*/
public function command_Warn(array $chatCallback, Player $player) {
$params = explode(' ', $chatCallback[1][2], 3);
if (count($params) <= 1) {
$this->maniaControl->getChat()->sendUsageInfo("No Login given! Example: '//warn login'", $player->login);
return;
}
$targetLogin = $params[1];
$this->maniaControl->getPlayerManager()->getPlayerActions()->warnPlayer($player->login, $targetLogin);
}
/**
* Handle //forcespec command
*
* @param array $chat
* @param Player $player
*/
public function command_ForceSpectator(array $chat, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, PlayerActions::SETTING_PERMISSION_FORCE_PLAYER_SPEC)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$params = explode(' ', $chat[1][2]);
if (count($params) <= 1) {
$this->maniaControl->getChat()->sendUsageInfo("No Login given! Example: '//forcespec login'", $player->login);
return;
}
$targetLogin = $params[1];
if (isset($params[2]) && is_numeric($params[2])) {
$type = (int)$params[2];
$this->maniaControl->getPlayerManager()->getPlayerActions()->forcePlayerToSpectator($player->login, $targetLogin, $type);
} else {
$this->maniaControl->getPlayerManager()->getPlayerActions()->forcePlayerToSpectator($player->login, $targetLogin);
}
}
/**
* Handle //forceplay command
*
* @param array $chat
* @param Player $player
*/
public function command_ForcePlay(array $chat, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, PlayerActions::SETTING_PERMISSION_FORCE_PLAYER_PLAY)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$params = explode(' ', $chat[1][2]);
if (!isset($params[1])) {
$this->maniaControl->getChat()->sendUsageInfo("No Login given! Example: '//forceplay login'", $player->login);
return;
}
$targetLogin = $params[1];
$type = 2;
if (isset($params[2]) && is_numeric($params[2])) {
$type = (int)$params[2];
}
$selectable = ($type === 2);
$this->maniaControl->getPlayerManager()->getPlayerActions()->forcePlayerToPlay($player->login, $targetLogin, $selectable);
}
/**
* Handle //forceblue command
*
* @param array $chat
* @param Player $player
*/
public function command_ForceBlue(array $chat, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, PlayerActions::SETTING_PERMISSION_FORCE_PLAYER_TEAM)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$params = explode(' ', $chat[1][2]);
if (!isset($params[1])) {
$this->maniaControl->getChat()->sendUsageInfo("No Login given! Example: '//forceblue login'", $player->login);
return;
}
$targetLogin = $params[1];
$this->maniaControl->getPlayerManager()->getPlayerActions()->forcePlayerToTeam($player->login, $targetLogin, PlayerActions::TEAM_BLUE);
}
/**
* Handle //forcered command
*
* @param array $chat
* @param Player $player
*/
public function command_ForceRed(array $chat, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, PlayerActions::SETTING_PERMISSION_FORCE_PLAYER_TEAM)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$params = explode(' ', $chat[1][2]);
if (!isset($params[1])) {
$this->maniaControl->getChat()->sendUsageInfo("No Login given! Example: '//forcered login'", $player->login);
return;
}
$targetLogin = $params[1];
$this->maniaControl->getPlayerManager()->getPlayerActions()->forcePlayerToTeam($player->login, $targetLogin, PlayerActions::TEAM_RED);
}
/**
* Handle //addfakeplayers command
*
* @param array $chatCallback
* @param Player $player
*/
public function command_AddFakePlayers(array $chatCallback, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_ADD_BOT)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$amount = 1;
$messageParts = explode(' ', $chatCallback[1][2]);
if (isset($messageParts[1]) && is_numeric($messageParts[1])) {
$amount = intval($messageParts[1]);
}
try {
for ($i = 0; $i < $amount; $i++) {
$this->maniaControl->getClient()->connectFakePlayer();
}
$this->maniaControl->getChat()->sendSuccess('Fake players connected!', $player);
} catch (UnavailableFeatureException $e) {
$this->maniaControl->getChat()->sendSuccess('Error while connecting a Fake-Player.', $player);
}
}
/**
* Handle //removefakeplayers command
*
* @param array $chatCallback
* @param Player $player
*/
public function command_RemoveFakePlayers(array $chatCallback, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_ADD_BOT)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$this->maniaControl->getClient()->disconnectFakePlayer('*');
$this->maniaControl->getChat()->sendSuccess('Fake players disconnected!', $player);
}
/**
* Handle //mute Command
*
* @param array $chatCallback
* @param Player $admin
*/
public function command_MutePlayer(array $chatCallback, Player $admin) {
$commandParts = explode(' ', $chatCallback[1][2]);
if (count($commandParts) <= 1) {
$this->maniaControl->getChat()->sendUsageInfo("No login specified! Example: '//mute login'", $admin);
return;
}
$targetLogin = $commandParts[1];
$this->maniaControl->getPlayerManager()->getPlayerActions()->mutePlayer($admin->login, $targetLogin);
}
/**
* Handle //unmute Command
*
* @param array $chatCallback
* @param Player $admin
*/
public function command_UnmutePlayer(array $chatCallback, Player $admin) {
$commandParts = explode(' ', $chatCallback[1][2]);
if (count($commandParts) <= 1) {
$this->maniaControl->getChat()->sendUsageInfo("No login specified! Example: '//unmute login'", $admin);
return;
}
$targetLogin = $commandParts[1];
$this->maniaControl->getPlayerManager()->getPlayerActions()->unMutePlayer($admin->login, $targetLogin);
}
/**
* Handle Player list command
*
* @param array $chatCallback
* @param Player $player
*/
public function command_playerList(array $chatCallback, Player $player) {
$this->maniaControl->getPlayerManager()->getPlayerList()->addPlayerToShownList($player, PlayerList::SHOWN_MAIN_WINDOW);
$this->maniaControl->getPlayerManager()->getPlayerList()->showPlayerList($player);
}
}

View File

@ -0,0 +1,352 @@
<?php
namespace ManiaControl\Players;
use ManiaControl\ManiaControl;
use ManiaControl\Utils\ClassUtil;
/**
* Player Data Manager
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class PlayerDataManager {
/*
* Constants
*/
const TABLE_PLAYERDATAMETADATA = 'mc_playerdata_metadata';
const TABLE_PLAYERDATA = 'mc_playerdata';
const TYPE_STRING = 'string';
const TYPE_INT = 'int';
const TYPE_REAL = 'real';
const TYPE_BOOL = 'bool';
const TYPE_ARRAY = 'array';
const ARRAY_DELIMITER = ';;';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
private $metaData = array();
private $storedData = array();
/**
* Construct a new player manager instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
$this->initTables();
// Store Stats MetaData
$this->storeMetaData();
}
/**
* Initialize necessary database tables
*
* @return bool
*/
private function initTables() {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$defaultType = "'" . self::TYPE_STRING . "'";
$typeSet = $defaultType . ",'" . self::TYPE_INT . "','" . self::TYPE_REAL . "','" . self::TYPE_BOOL . "','" . self::TYPE_ARRAY . "'";
$query = "CREATE TABLE IF NOT EXISTS `" . self::TABLE_PLAYERDATAMETADATA . "` (
`dataId` int(11) NOT NULL AUTO_INCREMENT,
`class` varchar(100) NOT NULL,
`dataName` varchar(100) NOT NULL,
`type` set({$typeSet}) NOT NULL DEFAULT {$defaultType},
`defaultValue` varchar(150) NOT NULL,
`description` varchar(150) NOT NULL,
PRIMARY KEY (`dataId`),
UNIQUE KEY `name` (`class`, `dataName`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Player-Data MetaData' AUTO_INCREMENT=1;";
$statement = $mysqli->prepare($query);
if ($mysqli->error) {
trigger_error($mysqli->error, E_USER_ERROR);
return false;
}
$statement->execute();
if ($statement->error) {
trigger_error($statement->error, E_USER_ERROR);
return false;
}
$statement->close();
$query = "CREATE TABLE IF NOT EXISTS `" . self::TABLE_PLAYERDATA . "` (
`index` int(11) NOT NULL AUTO_INCREMENT,
`serverIndex` int(11) NOT NULL,
`playerId` int(11) NOT NULL,
`dataId` int(11) NOT NULL,
`value` varchar(150) NOT NULL,
`changed` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`index`),
UNIQUE KEY `unique` (`dataId`,`playerId`,`serverIndex`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Player Data' AUTO_INCREMENT=1;";
$statement = $mysqli->prepare($query);
if ($mysqli->error) {
trigger_error($mysqli->error, E_USER_ERROR);
return false;
}
$statement->execute();
if ($statement->error) {
trigger_error($statement->error, E_USER_ERROR);
return false;
}
$statement->close();
return true;
}
/**
* Store Meta Data from the Database in the Ram
*/
private function storeMetaData() {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$query = "SELECT * FROM `" . self::TABLE_PLAYERDATAMETADATA . "`;";
$result = $mysqli->query($query);
if (!$result) {
trigger_error($mysqli->error);
return;
}
while ($row = $result->fetch_object()) {
$this->metaData[$row->class . $row->dataName] = $row;
}
$result->free();
}
/**
* Destroys the stored PlayerData (Method get called by PlayerManager, so don't call it anywhere else)
*
* @param Player $player
*/
public function destroyPlayerData(Player $player) {
unset($this->storedData[$player->index]);
}
/**
* Defines the Player-Data MetaData
*
* @param mixed $object
* @param string $dataName
* @param mixed $default
* @param string $dataDescription (optional)
* @return bool
*/
public function defineMetaData($object, $dataName, $default, $dataDescription = '') {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$className = ClassUtil::getClass($object);
$query = "INSERT INTO `" . self::TABLE_PLAYERDATAMETADATA . "` (
`class`,
`dataName`,
`type`,
`defaultValue`,
`description`
) VALUES (
?, ?, ?, ?, ?
) ON DUPLICATE KEY UPDATE
`type` = VALUES(`type`),
`defaultValue` = VALUES(`defaultValue`),
`description` = VALUES(`description`);";
$statement = $mysqli->prepare($query);
if ($mysqli->error) {
trigger_error($mysqli->error);
return false;
}
$type = $this->getType($default);
$statement->bind_param('sssss', $className, $dataName, $type, $default, $dataDescription);
$statement->execute();
if ($statement->error) {
trigger_error($statement->error);
$statement->close();
return false;
}
$statement->close();
return true;
}
/**
* Get Type of a Parameter
*
* @param mixed $param
* @return string
*/
private function getType($param) {
if (is_int($param)) {
return self::TYPE_INT;
}
if (is_real($param)) {
return self::TYPE_REAL;
}
if (is_bool($param)) {
return self::TYPE_BOOL;
}
if (is_string($param)) {
return self::TYPE_STRING;
}
if (is_array($param)) {
return self::TYPE_ARRAY;
}
trigger_error('Unsupported data type. ' . print_r($param, true));
return null;
}
/**
* Gets the Player Data
*
* @param mixed $object
* @param string $dataName
* @param Player $player
* @param int $serverIndex
* @return mixed
*/
public function getPlayerData($object, $dataName, Player $player, $serverIndex = -1) {
$className = ClassUtil::getClass($object);
$meta = $this->metaData[$className . $dataName];
// Check if data is already in the ram
if (isset($this->storedData[$player->index]) && isset($this->storedData[$player->index][$meta->dataId])) {
return $this->storedData[$player->index][$meta->dataId];
}
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$dataQuery = "SELECT `value` FROM `" . self::TABLE_PLAYERDATA . "`
WHERE `dataId` = ?
AND `playerId` = ?
AND `serverIndex` = ?;";
$dataStatement = $mysqli->prepare($dataQuery);
if ($mysqli->error) {
trigger_error($mysqli->error);
return null;
}
$dataStatement->bind_param('iii', $meta->dataId, $player->index, $serverIndex);
$dataStatement->execute();
if ($dataStatement->error) {
trigger_error($dataStatement->error);
return null;
}
$dataStatement->store_result();
if ($dataStatement->num_rows <= 0) {
$this->setPlayerData($object, $dataName, $player, $meta->defaultValue, $serverIndex);
return $meta->defaultValue;
}
$dataStatement->bind_result($value);
$dataStatement->fetch();
$dataStatement->free_result();
$dataStatement->close();
$data = $this->castSetting($meta->type, $value);
// Store setting in the ram
if (!isset($this->storedData[$player->index])) {
$this->storedData[$player->index] = array();
}
$this->storedData[$player->index][$meta->dataId] = $data;
return $data;
}
/**
* Set a PlayerData to a specific defined statMetaData
*
* @param mixed $object
* @param string $dataName
* @param Player $player
* @param mixed $value
* @param int $serverIndex (empty if it's global)
* @return bool
*/
public function setPlayerData($object, $dataName, Player $player, $value, $serverIndex = -1) {
$className = ClassUtil::getClass($object);
if (!$player) {
return false;
}
$dataId = $this->getMetaDataId($className, $dataName);
if (!$dataId) {
return false;
}
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$query = "INSERT INTO `" . self::TABLE_PLAYERDATA . "` (
`serverIndex`,
`playerId`,
`dataId`,
`value`
) VALUES (
?, ?, ?, ?
) ON DUPLICATE KEY UPDATE
`value` = VALUES(`value`);";
$statement = $mysqli->prepare($query);
if ($mysqli->error) {
trigger_error($mysqli->error);
return false;
}
$statement->bind_param('iiis', $serverIndex, $player->index, $dataId, $value);
$statement->execute();
if ($statement->error) {
trigger_error($statement->error);
$statement->close();
return false;
}
$statement->close();
// Store changed value
if (!isset($this->storedData[$player->index])) {
$this->storedData[$player->index] = array();
}
$this->storedData[$player->index][$dataId] = $value;
return true;
}
/**
* Return the Id of the MetaData
*
* @param string $className
* @param string $statName
* @return int
*/
private function getMetaDataId($className, $statName) {
if (isset($this->metaData[$className . $statName])) {
$stat = $this->metaData[$className . $statName];
return (int)$stat->dataId;
}
return null;
}
/**
* Cast a Setting to the given Type
*
* @param string $type
* @param mixed $value
* @return mixed
*/
private function castSetting($type, $value) {
if ($type === self::TYPE_INT) {
return (int)$value;
}
if ($type === self::TYPE_REAL) {
return (float)$value;
}
if ($type === self::TYPE_BOOL) {
return (bool)$value;
}
if ($type === self::TYPE_STRING) {
return (string)$value;
}
if ($type === self::TYPE_ARRAY) {
return explode(self::ARRAY_DELIMITER, $value);
}
trigger_error('Unsupported data type. ' . print_r($type, true));
return $value;
}
}

View File

@ -0,0 +1,278 @@
<?php
namespace ManiaControl\Players;
use FML\Controls\Frame;
use FML\Controls\Labels\Label_Button;
use FML\Controls\Labels\Label_Text;
use FML\Controls\Quad;
use FML\Controls\Quads\Quad_BgsPlayerCard;
use FML\ManiaLink;
use FML\Script\Script;
use ManiaControl\ManiaControl;
use ManiaControl\Manialinks\ManialinkManager;
use ManiaControl\Statistics\StatisticManager;
use ManiaControl\Utils\Formatter;
/**
* Player Detailed Page
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class PlayerDetailed {
/*
* Constants
*/
const STATS_PER_COLUMN = 13;
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/**
* Create a new Player Detailed Instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Settings
$this->width = $this->maniaControl->getManialinkManager()->getStyleManager()->getListWidgetsWidth();
$this->height = $this->maniaControl->getManialinkManager()->getStyleManager()->getListWidgetsHeight();
$this->quadStyle = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultMainWindowStyle();
$this->quadSubstyle = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultMainWindowSubStyle();
}
/**
* Show a Frame with detailed Information about the Target Player
*
* @param Player $player
* @param string $targetLogin
*/
public function showPlayerDetailed(Player $player, $targetLogin) {
/** @var Player $target */
$target = $this->maniaControl->getPlayerManager()->getPlayer($targetLogin);
// Create ManiaLink
$maniaLink = new ManiaLink(ManialinkManager::MAIN_MLID);
$script = $maniaLink->getScript();
// Main frame
$frame = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultListFrame($script);
$maniaLink->add($frame);
// Create script and features
$script = new Script();
$maniaLink->setScript($script);
$posY = $this->height / 2 - 7;
//Nation Quad
$countryQuad = new Quad();
$frame->add($countryQuad);
$countryQuad->setImage("file://ZoneFlags/Login/{$targetLogin}/country");
$countryQuad->setPosition(-$this->width / 2 + 10, $posY);
$countryQuad->setSize(5, 5);
$countryQuad->setZ(-0.1);
$countryQuad->setHAlign($countryQuad::LEFT);
//Nickname
$label = new Label_Text();
$frame->add($label);
$label->setPosition(-$this->width / 2 + 15, $posY);
$label->setText($target->nickname);
$label->setHAlign($label::LEFT);
//Define MainLabel (Login)
$posY -= 8;
$mainLabel = new Label_Text();
$frame->add($mainLabel);
$mainLabel->setPosition(-$this->width / 2 + 10, $posY);
$mainLabel->setTextSize(1.2);
$mainLabel->setHAlign($mainLabel::LEFT);
$mainLabel->setText('Login: ');
$posY -= 5;
$label = clone $mainLabel;
$frame->add($label);
$label->setY($posY);
$label->setText('Nation: ');
$posY -= 5;
$label = clone $mainLabel;
$frame->add($label);
$label->setY($posY);
$label->setText('Province: ');
$posY -= 5;
$label = clone $mainLabel;
$frame->add($label);
$label->setY($posY);
$label->setText('Authorization: ');
$posY -= 5;
$label = clone $mainLabel;
$frame->add($label);
$label->setY($posY);
$label->setText("Ladder Rank:");
$posY -= 5;
$label = clone $mainLabel;
$frame->add($label);
$label->setY($posY);
$label->setText('Ladder Score: ');
$posY -= 5;
$label = clone $mainLabel;
$frame->add($label);
$label->setY($posY);
$label->setText('Inscribed Zone: ');
$posY -= 5;
$label = clone $mainLabel;
$frame->add($label);
$label->setY($posY);
$label->setText('Avatar');
//Login
$posY = $this->height / 2 - 15;
$mainLabel = new Label_Text();
$frame->add($mainLabel);
$mainLabel->setPosition(-$this->width / 2 + 30, $posY);
$mainLabel->setText($target->login);
$mainLabel->setTextSize(1.2);
$mainLabel->setHAlign($mainLabel::LEFT);
//Country
$posY -= 5;
$label = clone $mainLabel;
$frame->add($label);
$label->setY($posY);
$label->setText($target->getCountry());
//Province
$posY -= 5;
$label = clone $mainLabel;
$frame->add($label);
$label->setY($posY);
$label->setText($target->getProvince());
//AuthLevel
$posY -= 5;
$label = clone $mainLabel;
$frame->add($label);
$label->setY($posY);
$label->setText($this->maniaControl->getAuthenticationManager()->getAuthLevelName($target->authLevel));
//LadderRank
$posY -= 5;
$label = clone $mainLabel;
$frame->add($label);
$label->setY($posY);
$label->setText($target->ladderRank);
//LadderScore
$posY -= 5;
$label = clone $mainLabel;
$frame->add($label);
$label->setY($posY);
$label->setText(round($target->ladderScore, 2));
//Played Since
$posY -= 5;
$label = clone $mainLabel;
$frame->add($label);
$label->setY($posY);
$label->setText(date('d M Y', time() - 3600 * 24 * $target->daysSinceZoneInscription));
$quad = new Quad();
$frame->add($quad);
$quad->setImage('file://Avatars/' . $targetLogin . "/default");
$quad->setPosition(-$this->width / 2 + 50, -$this->height / 2 + 34);
$quad->setAlign($quad::RIGHT, $quad::TOP);
$quad->setSize(20, 20);
//Statistics
$frame->add($this->statisticsFrame($target));
$quad = new Label_Button();
$frame->add($quad);
$quad->setStyle($quad::STYLE_CardMain_Quit);
$quad->setHAlign($quad::LEFT);
$quad->setScale(0.75);
$quad->setText('Back');
$quad->setPosition(-$this->width / 2 + 7, -$this->height / 2 + 7);
$quad->setAction(PlayerCommands::ACTION_OPEN_PLAYERLIST);
// render and display xml
$this->maniaControl->getManialinkManager()->displayWidget($maniaLink, $player, 'PlayerDetailed');
}
/**
* Build a Frame with Statistics about the given Player
*
* @param Player $player
* @return Frame
*/
public function statisticsFrame(Player $player) {
$frame = new Frame();
$playerStats = $this->maniaControl->getStatisticManager()->getAllPlayerStats($player);
$posY = $this->height / 2 - 15;
$posX = -$this->width / 2 + 52;
$index = 1;
foreach ($playerStats as $stat) {
$value = (float)$stat[1];
if (!$value) {
continue;
}
$statProperties = $stat[0];
if ($statProperties->type === StatisticManager::STAT_TYPE_TIME) {
$value = Formatter::formatTimeH($value);
} else if ($statProperties->type === StatisticManager::STAT_TYPE_FLOAT) {
$value = round($value, 2);
}
if ($index % 2 !== 0) {
$lineQuad = new Quad_BgsPlayerCard();
$frame->add($lineQuad);
$lineQuad->setSize(49, 4);
$lineQuad->setSubStyle($lineQuad::SUBSTYLE_BgPlayerCardBig);
$lineQuad->setPosition($posX, $posY, 0.001);
$lineQuad->setHAlign($lineQuad::LEFT);
}
$label = new Label_Text();
$frame->add($label);
$label->setPosition($posX + 4, $posY);
$label->setText($statProperties->name);
$label->setHAlign($label::LEFT);
$label->setTextSize(1.5);
$label = new Label_Text();
$frame->add($label);
$label->setPosition($posX + 40, $posY);
$label->setText($value);
$label->setTextSize(1.5);
$posY -= 4;
$index++;
if ($index > self::STATS_PER_COLUMN) {
$posY = $this->height / 2 - 15;
$posX += 47;
$index = 0;
}
}
return $frame;
}
}

788
core/Players/PlayerList.php Normal file
View File

@ -0,0 +1,788 @@
<?php
namespace ManiaControl\Players;
use FML\Controls\Frame;
use FML\Controls\Labels\Label_Text;
use FML\Controls\Quad;
use FML\Controls\Quads\Quad_BgRaceScore2;
use FML\Controls\Quads\Quad_BgsPlayerCard;
use FML\Controls\Quads\Quad_Emblems;
use FML\Controls\Quads\Quad_Icons64x64_1;
use FML\Controls\Quads\Quad_UIConstruction_Buttons;
use FML\ManiaLink;
use FML\Script\Features\Paging;
use ManiaControl\Admin\AuthenticationManager;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\CallbackManager;
use ManiaControl\Callbacks\TimerListener;
use ManiaControl\ManiaControl;
use ManiaControl\Manialinks\ManialinkManager;
use ManiaControl\Manialinks\ManialinkPageAnswerListener;
use ManiaControl\Utils\Formatter;
use Maniaplanet\DedicatedServer\Xmlrpc\PlayerStateException;
use Maniaplanet\DedicatedServer\Xmlrpc\UnknownPlayerException;
use MCTeam\CustomVotesPlugin;
/**
* PlayerList Widget Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class PlayerList implements ManialinkPageAnswerListener, CallbackListener, TimerListener {
/*
* Constants
*/
const ACTION_FORCE_RED = 'PlayerList.ForceRed';
const ACTION_FORCE_BLUE = 'PlayerList.ForceBlue';
const ACTION_FORCE_SPEC = 'PlayerList.ForceSpec';
const ACTION_FORCE_SPEC_VOTE = 'PlayerList.ForceSpecVote';
const ACTION_FORCE_PLAY = 'PlayerList.ForcePlay';
const ACTION_PLAYER_ADV = 'PlayerList.PlayerAdvancedActions';
const ACTION_CLOSE_PLAYER_ADV = 'PlayerList.ClosePlayerAdvWidget';
const ACTION_MUTE_PLAYER = 'PlayerList.MutePlayer';
const ACTION_UNMUTE_PLAYER = 'PlayerList.UnMutePlayer';
const ACTION_WARN_PLAYER = 'PlayerList.WarnPlayer';
const ACTION_KICK_PLAYER = 'PlayerList.KickPlayer';
const ACTION_KICK_PLAYER_VOTE = 'PlayerList.KickPlayerVote';
const ACTION_BAN_PLAYER = 'PlayerList.BanPlayer';
const ACTION_ADD_AS_MASTER = 'PlayerList.PlayerAddAsMaster';
const ACTION_ADD_AS_ADMIN = 'PlayerList.PlayerAddAsAdmin';
const ACTION_ADD_AS_MOD = 'PlayerList.PlayerAddAsModerator';
const ACTION_REVOKE_RIGHTS = 'PlayerList.RevokeRights';
const ACTION_OPEN_PLAYER_DETAILED = 'PlayerList.OpenPlayerDetailed';
const ACTION_SPECTATE_PLAYER = 'PlayerList.SpectatePlayer';
const DEFAULT_CUSTOM_VOTE_PLUGIN = 'MCTeam\CustomVotesPlugin';
const SHOWN_MAIN_WINDOW = -1;
const MAX_PLAYERS_PER_PAGE = 15;
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
private $playersListShown = array();
/**
* Construct a new PlayerList instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Callbacks
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ACTION_CLOSE_PLAYER_ADV, $this, 'closePlayerAdvancedWidget');
$this->maniaControl->getCallbackManager()->registerCallbackListener(ManialinkManager::CB_MAIN_WINDOW_CLOSED, $this, 'closeWidget');
$this->maniaControl->getCallbackManager()->registerCallbackListener(ManialinkManager::CB_MAIN_WINDOW_OPENED, $this, 'handleWidgetOpened');
$this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_PLAYERMANIALINKPAGEANSWER, $this, 'handleManialinkPageAnswer');
// Update Widget Events
$this->maniaControl->getCallbackManager()->registerCallbackListener(PlayerManager::CB_PLAYERINFOCHANGED, $this, 'updateWidget');
$this->maniaControl->getCallbackManager()->registerCallbackListener(PlayerManager::CB_PLAYERDISCONNECT, $this, 'updateWidget');
$this->maniaControl->getCallbackManager()->registerCallbackListener(PlayerManager::CB_PLAYERCONNECT, $this, 'updateWidget');
$this->maniaControl->getCallbackManager()->registerCallbackListener(AuthenticationManager::CB_AUTH_LEVEL_CHANGED, $this, 'updateWidget');
}
/**
* Add Player to Shown List
*
* @param Player $player
* @param int $showStatus
*/
public function addPlayerToShownList(Player $player, $showStatus = self::SHOWN_MAIN_WINDOW) {
$this->playersListShown[$player->login] = $showStatus;
}
/**
* Unset the player if he opened another Main Widget
*
* @param Player $player
* @param $openedWidget
*/
public function handleWidgetOpened(Player $player, $openedWidget) {
//unset when another main widget got opened
if ($openedWidget !== 'PlayerList') {
unset($this->playersListShown[$player->login]);
}
}
/**
* Closes the widget
*
* @param Player $player
*/
public function closeWidget(Player $player) {
unset($this->playersListShown[$player->login]);
}
/**
* Closes the player advanced widget widget
*
* @param array $callback
* @param Player $player
*/
public function closePlayerAdvancedWidget(array $callback, Player $player) {
$this->playersListShown[$player->login] = self::SHOWN_MAIN_WINDOW;
$this->showPlayerList($player); // overwrite the manialink
}
/**
* Show the PlayerList Widget to the Player
*
* @param Player $player
*/
public function showPlayerList(Player $player) {
$width = $this->maniaControl->getManialinkManager()->getStyleManager()->getListWidgetsWidth();
$height = $this->maniaControl->getManialinkManager()->getStyleManager()->getListWidgetsHeight();
// get PlayerList
$players = $this->maniaControl->getPlayerManager()->getPlayers();
//create manialink
$maniaLink = new ManiaLink(ManialinkManager::MAIN_MLID);
$script = $maniaLink->getScript();
$paging = new Paging();
$script->addFeature($paging);
// Main frame
$frame = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultListFrame($script, $paging);
$maniaLink->add($frame);
// Start offsets
$posX = -$width / 2;
$posY = $height / 2;
// Predefine Description Label
$descriptionLabel = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultDescriptionLabel();
$frame->add($descriptionLabel);
// Headline
$headFrame = new Frame();
$frame->add($headFrame);
$headFrame->setY($posY - 5);
$labelLineArray = array('Id' => $posX + 5, 'Nickname' => $posX + 18, 'Login' => $posX + 70, 'Location' => $posX + 101);
if ($this->maniaControl->getAuthenticationManager()->checkRight($player, AuthenticationManager::AUTH_LEVEL_MODERATOR)
) {
$labelLineArray['Actions'] = $posX + 135;
}
$this->maniaControl->getManialinkManager()->labelLine($headFrame, $labelLineArray);
$index = 1;
$posY = $height / 2 - 10;
$pageFrame = null;
foreach ($players as $listPlayer) {
if ($index % self::MAX_PLAYERS_PER_PAGE === 1) {
$pageFrame = new Frame();
$frame->add($pageFrame);
$paging->addPage($pageFrame);
$posY = $height / 2 - 10;
}
$path = $listPlayer->getProvince();
$playerFrame = new Frame();
$pageFrame->add($playerFrame);
if ($index % 2 !== 0) {
$lineQuad = new Quad_BgsPlayerCard();
$playerFrame->add($lineQuad);
$lineQuad->setSize($width, 4);
$lineQuad->setSubStyle($lineQuad::SUBSTYLE_BgPlayerCardBig);
$lineQuad->setZ(0.001);
}
$array = array($index => $posX + 5, $listPlayer->nickname => $posX + 18, $listPlayer->login => $posX + 70, $path => $posX + 101);
$this->maniaControl->getManialinkManager()->labelLine($playerFrame, $array);
$playerFrame->setY($posY);
// Show current Player Arrow
if ($listPlayer->index === $player->index) {
$currentQuad = new Quad_Icons64x64_1();
$playerFrame->add($currentQuad);
$currentQuad->setX($posX + 3.5);
$currentQuad->setZ(0.2);
$currentQuad->setSize(4, 4);
$currentQuad->setSubStyle($currentQuad::SUBSTYLE_ArrowBlue);
}
// Team Emblem
if ($listPlayer->teamId >= 0) {
// Player is in a Team
$teamQuad = new Quad_Emblems();
$playerFrame->add($teamQuad);
$teamQuad->setX($posX + 10);
$teamQuad->setZ(0.1);
$teamQuad->setSize(3.8, 3.8);
switch ($listPlayer->teamId) {
case 0:
$teamQuad->setSubStyle($teamQuad::SUBSTYLE_1);
break;
case 1:
$teamQuad->setSubStyle($teamQuad::SUBSTYLE_2);
break;
}
} else if ($listPlayer->isSpectator) {
// Player is in Spectator Mode
$specQuad = new Quad_BgRaceScore2();
$playerFrame->add($specQuad);
$specQuad->setX($posX + 10);
$specQuad->setZ(0.1);
$specQuad->setSubStyle($specQuad::SUBSTYLE_Spectator);
$specQuad->setSize(3.8, 3.8);
}
$countryCode = Formatter::mapCountry($listPlayer->getCountry());
if ($countryCode !== 'OTH') {
// Nation Quad
$countryQuad = new Quad();
$playerFrame->add($countryQuad);
$countryQuad->setImage("file://ZoneFlags/Login/{$listPlayer->login}/country");
$countryQuad->setX($posX + 98);
$countryQuad->setZ(1);
$countryQuad->setSize(4, 4);
$countryQuad->addTooltipLabelFeature($descriptionLabel, '$<' . $listPlayer->nickname . '$> from ' . $listPlayer->path);
}
// Level Quad
$rightQuad = new Quad_BgRaceScore2();
$playerFrame->add($rightQuad);
$rightQuad->setX($posX + 13);
$rightQuad->setZ(3);
$rightQuad->setSize(7, 3.5);
$rightQuad->setSubStyle($rightQuad::SUBSTYLE_CupFinisher);
$rightLabel = new Label_Text();
$playerFrame->add($rightLabel);
$rightLabel->setX($posX + 13.9);
$rightLabel->setZ(3.1);
$rightLabel->setText($this->maniaControl->getAuthenticationManager()->getAuthLevelAbbreviation($listPlayer->authLevel));
$rightLabel->setTextSize(0.8);
$rightLabel->setTextColor('fff');
$description = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($listPlayer) . ' ' . $listPlayer->nickname;
$rightLabel->addTooltipLabelFeature($descriptionLabel, $description);
// Player Statistics
$playerQuad = new Quad_Icons64x64_1();
$playerFrame->add($playerQuad);
$playerQuad->setX($posX + 61);
$playerQuad->setZ(3);
$playerQuad->setSize(2.7, 2.7);
$playerQuad->setSubStyle($playerQuad::SUBSTYLE_TrackInfo);
$playerQuad->setAction(self::ACTION_OPEN_PLAYER_DETAILED . '.' . $listPlayer->login);
$description = 'View Statistics of $<' . $listPlayer->nickname . '$>';
$playerQuad->addTooltipLabelFeature($descriptionLabel, $description);
// Camera Quad
$playerQuad = new Quad_UIConstruction_Buttons();
$playerFrame->add($playerQuad);
$playerQuad->setX($posX + 64.5);
$playerQuad->setZ(3);
$playerQuad->setSize(3.8, 3.8);
$playerQuad->setSubStyle($playerQuad::SUBSTYLE_Camera);
$description = 'Spectate $<' . $listPlayer->nickname . '$>';
$playerQuad->addTooltipLabelFeature($descriptionLabel, $description);
$playerQuad->setAction(self::ACTION_SPECTATE_PLAYER . '.' . $listPlayer->login);
// Player Profile Quad
$playerQuad = new Quad_UIConstruction_Buttons();
$playerFrame->add($playerQuad);
$playerQuad->setX($posX + 68);
$playerQuad->setZ(3);
$playerQuad->setSize(3.8, 3.8);
$playerQuad->setSubStyle($playerQuad::SUBSTYLE_Author);
$playerQuad->addPlayerProfileFeature($listPlayer->login);
// Description Label
$description = 'View Player Profile of $<' . $listPlayer->nickname . '$>';
$playerQuad->addTooltipLabelFeature($descriptionLabel, $description);
if ($this->maniaControl->getAuthenticationManager()->checkRight($player, AuthenticationManager::AUTH_LEVEL_MODERATOR)
) {
// Further Player actions Quad
$playerQuad = new Quad_Icons64x64_1();
$playerFrame->add($playerQuad);
$playerQuad->setX($posX + 132);
$playerQuad->setZ(0.1);
$playerQuad->setSize(3.8, 3.8);
$playerQuad->setSubStyle($playerQuad::SUBSTYLE_Buddy);
$playerQuad->setAction(self::ACTION_PLAYER_ADV . '.' . $listPlayer->login);
// Description Label
$description = 'Advanced Player Actions for $<' . $listPlayer->nickname . '$>';
$playerQuad->addTooltipLabelFeature($descriptionLabel, $description);
}
if ($this->maniaControl->getServer()->isTeamMode()
) {
if ($this->maniaControl->getAuthenticationManager()->checkPermission($player, PlayerActions::SETTING_PERMISSION_FORCE_PLAYER_TEAM)
) {
// Force to Red-Team Quad
$redQuad = new Quad_Emblems();
$playerFrame->add($redQuad);
$redQuad->setX($posX + 145);
$redQuad->setZ(0.1);
$redQuad->setSize(3.8, 3.8);
$redQuad->setSubStyle($redQuad::SUBSTYLE_2);
$redQuad->setAction(self::ACTION_FORCE_RED . '.' . $listPlayer->login);
// Force to Red-Team Description Label
$description = 'Force $<' . $listPlayer->nickname . '$> to Red Team!';
$redQuad->addTooltipLabelFeature($descriptionLabel, $description);
// Force to Blue-Team Quad
$blueQuad = new Quad_Emblems();
$playerFrame->add($blueQuad);
$blueQuad->setX($posX + 141);
$blueQuad->setZ(0.1);
$blueQuad->setSize(3.8, 3.8);
$blueQuad->setSubStyle($blueQuad::SUBSTYLE_1);
$blueQuad->setAction(self::ACTION_FORCE_BLUE . '.' . $listPlayer->login);
// Force to Blue-Team Description Label
$description = 'Force $<' . $listPlayer->nickname . '$> to Blue Team!';
$blueQuad->addTooltipLabelFeature($descriptionLabel, $description);
} else if ($this->maniaControl->getPluginManager()->isPluginActive(self::DEFAULT_CUSTOM_VOTE_PLUGIN)
) {
// Kick Player Vote
$kickQuad = new Quad_UIConstruction_Buttons();
$playerFrame->add($kickQuad);
$kickQuad->setX($posX + 141);
$kickQuad->setZ(0.1);
$kickQuad->setSize(3.8, 3.8);
$kickQuad->setSubStyle($kickQuad::SUBSTYLE_Validate_Step2);
$kickQuad->setAction(self::ACTION_KICK_PLAYER_VOTE . '.' . $listPlayer->login);
$description = 'Start a Kick Vote on $<' . $listPlayer->nickname . '$>!';
$kickQuad->addTooltipLabelFeature($descriptionLabel, $description);
}
} else {
if ($this->maniaControl->getAuthenticationManager()->checkPermission($player, PlayerActions::SETTING_PERMISSION_FORCE_PLAYER_PLAY)
) {
// Force to Play
$playQuad = new Quad_Emblems();
$playerFrame->add($playQuad);
$playQuad->setX($posX + 143);
$playQuad->setZ(0.1);
$playQuad->setSize(3.8, 3.8);
$playQuad->setSubStyle($playQuad::SUBSTYLE_2);
$playQuad->setAction(self::ACTION_FORCE_PLAY . '.' . $listPlayer->login);
$description = 'Force $<' . $listPlayer->nickname . '$> to Play!';
$playQuad->addTooltipLabelFeature($descriptionLabel, $description);
}
}
if ($this->maniaControl->getAuthenticationManager()->checkPermission($player, PlayerActions::SETTING_PERMISSION_FORCE_PLAYER_SPEC)
) {
// Force to Spectator Quad
$spectatorQuad = new Quad_BgRaceScore2();
$playerFrame->add($spectatorQuad);
$spectatorQuad->setX($posX + 137);
$spectatorQuad->setZ(0.1);
$spectatorQuad->setSize(3.8, 3.8);
$spectatorQuad->setSubStyle($spectatorQuad::SUBSTYLE_Spectator);
$spectatorQuad->setAction(self::ACTION_FORCE_SPEC . '.' . $listPlayer->login);
// Force to Spectator Description Label
$description = 'Force $<' . $listPlayer->nickname . '$> to Spectator!';
$spectatorQuad->addTooltipLabelFeature($descriptionLabel, $description);
} else if ($this->maniaControl->getPluginManager()->isPluginActive(self::DEFAULT_CUSTOM_VOTE_PLUGIN)
) {
// Force to Spectator Quad
$spectatorQuad = new Quad_BgRaceScore2();
$playerFrame->add($spectatorQuad);
$spectatorQuad->setX($posX + 137);
$spectatorQuad->setZ(0.1);
$spectatorQuad->setSize(3.8, 3.8);
$spectatorQuad->setSubStyle($spectatorQuad::SUBSTYLE_Spectator);
$spectatorQuad->setAction(self::ACTION_FORCE_SPEC_VOTE . '.' . $listPlayer->login);
// Force to Spectator Description Label
$description = 'Start a Vote to force $<' . $listPlayer->nickname . '$> to Spectator!';
$spectatorQuad->addTooltipLabelFeature($descriptionLabel, $description);
}
$posY -= 4;
$index++;
}
// Show advanced window
$listShownValue = $this->playersListShown[$player->login];
if ($listShownValue && $listShownValue !== self::SHOWN_MAIN_WINDOW) {
$frame = $this->showAdvancedPlayerWidget($player, $listShownValue);
$maniaLink->add($frame);
}
// Render and display xml
$this->maniaControl->getManialinkManager()->displayWidget($maniaLink, $player, 'PlayerList');
}
/**
* Extra window with special actions on players like warn,kick, ban, authorization levels...
*
* @param Player $admin
* @param string $login
* @return Frame
*/
public function showAdvancedPlayerWidget(Player $admin, $login) {
$player = $this->maniaControl->getPlayerManager()->getPlayer($login);
$width = $this->maniaControl->getManialinkManager()->getStyleManager()->getListWidgetsWidth();
$height = $this->maniaControl->getManialinkManager()->getStyleManager()->getListWidgetsHeight();
$quadStyle = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultMainWindowStyle();
$quadSubstyle = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultMainWindowSubStyle();
//Settings
$posX = $width / 2 + 2.5;
$width = 35;
$height = $height * 0.75;
$textSize = 1.5;
$textColor = 'fff';
$quadWidth = $width - 7;
// mainframe
$frame = new Frame();
$frame->setSize($width, $height);
$frame->setPosition($posX + $width / 2, 0);
// Add Close Quad (X)
$closeQuad = new Quad_Icons64x64_1();
$frame->add($closeQuad);
$closeQuad->setPosition($width * 0.4, $height * 0.43, 3);
$closeQuad->setSize(6, 6);
$closeQuad->setSubStyle($closeQuad::SUBSTYLE_QuitRace);
$closeQuad->setAction(self::ACTION_CLOSE_PLAYER_ADV);
// Background Quad
$backgroundQuad = new Quad();
$frame->add($backgroundQuad);
$backgroundQuad->setSize($width, $height);
$backgroundQuad->setImage('https://dl.dropboxusercontent.com/u/105352981/Stuff/CAM%20SM%20BORDER%20PNG.png'); //TODO just a test
//$backgroundQuad->setStyles($quadStyle, $quadSubstyle);
$backgroundQuad->setZ(0.2);
// Background Quad
$backgroundQuad = new Quad();
$frame->add($backgroundQuad);
$backgroundQuad->setSize($width - 2, $height - 2);
$backgroundQuad->setStyles($quadStyle, $quadSubstyle);
$backgroundQuad->setZ(0.1);
// Show headline
$label = new Label_Text();
$frame->add($label);
$label->setHAlign($label::LEFT);
$label->setX(-$width / 2 + 5);
$label->setY($height / 2 - 5);
$label->setStyle($label::STYLE_TextCardSmall);
$label->setTextSize($textSize);
$label->setText('Advanced Actions');
$label->setTextColor($textColor);
// Show Nickname
$label = new Label_Text();
$frame->add($label);
$label->setHAlign($label::LEFT);
$label->setX(0);
$label->setY($height / 2 - 8);
$label->setStyle($label::STYLE_TextCardSmall);
$label->setTextSize($textSize);
$label->setText($player->nickname);
$label->setTextColor($textColor);
// Mute Player
$posY = $height / 2 - 14;
$quad = new Quad_BgsPlayerCard();
$frame->add($quad);
$quad->setX(0);
$quad->setY($posY);
$quad->setSubStyle($quad::SUBSTYLE_BgPlayerCardBig);
$quad->setSize($quadWidth, 5);
$label = new Label_Text();
$frame->add($label);
$label->setX(0);
$label->setY($posY);
$label->setStyle($label::STYLE_TextCardSmall);
$label->setTextSize($textSize);
$label->setTextColor($textColor);
if (!$player->isMuted()) {
$label->setText('Mute');
$quad->setAction(self::ACTION_MUTE_PLAYER . '.' . $login);
} else {
$label->setText('UnMute');
$quad->setAction(self::ACTION_UNMUTE_PLAYER . '.' . $login);
}
// Warn Player
$posY -= 5;
$quad = clone $quad;
$frame->add($quad);
$quad->setY($posY);
$quad->setAction(self::ACTION_WARN_PLAYER . '.' . $login);
$label = clone $label;
$frame->add($label);
$label->setY($posY);
$label->setText('Warn');
$label->setTextColor($textColor);
$posY -= 5;
// Show Kick
$quad = clone $quad;
$frame->add($quad);
$quad->setY($posY);
$quad->setAction(self::ACTION_KICK_PLAYER . '.' . $login);
$label = clone $label;
$frame->add($label);
$label->setY($posY);
$label->setText('Kick');
$label->setTextColor('f90');
$posY -= 5;
// Show Ban
$quad = clone $quad;
$frame->add($quad);
$quad->setY($posY);
$quad->setAction(self::ACTION_BAN_PLAYER . '.' . $login);
$label = clone $label;
$frame->add($label);
$label->setY($posY);
$label->setText('Ban');
$label->setTextColor('700');
$posY -= 10;
// Show Add as Master-Admin
$quad = clone $quad;
$frame->add($quad);
$quad->setY($posY);
$quad->setAction(self::ACTION_ADD_AS_MASTER . '.' . $login);
$label = clone $label;
$frame->add($label);
$label->setY($posY);
$label->setText('Set SuperAdmin');
$label->setTextColor($textColor);
$posY -= 5;
// Show Add as Admin
$quad = clone $quad;
$frame->add($quad);
$quad->setY($posY);
$quad->setAction(self::ACTION_ADD_AS_ADMIN . '.' . $login);
$label = clone $label;
$frame->add($label);
$label->setY($posY);
$label->setText('Set Admin');
$label->setTextColor($textColor);
$posY -= 5;
// Show Add as Moderator
$quad = clone $quad;
$frame->add($quad);
$quad->setY($posY);
$quad->setAction(self::ACTION_ADD_AS_MOD . '.' . $login);
$label = clone $label;
$frame->add($label);
$label->setY($posY);
$label->setText('Set Moderator');
$label->setTextColor($textColor);
if ($player->authLevel > 0
&& $this->maniaControl->getAuthenticationManager()->checkRight($admin, $player->authLevel + 1)
) {
$posY -= 5;
// Revoke Rights
$quad = clone $quad;
$frame->add($quad);
$quad->setY($posY);
$quad->setAction(self::ACTION_REVOKE_RIGHTS . '.' . $login);
$label = clone $label;
$frame->add($label);
$label->setY($posY);
$label->setText('Revoke Rights');
$label->setTextColor('700');
}
return $frame;
}
/**
* Called on ManialinkPageAnswer
*
* @param array $callback
*/
public function handleManialinkPageAnswer(array $callback) {
$actionId = $callback[1][2];
$actionArray = explode('.', $actionId, 3);
if (count($actionArray) <= 2) {
return;
}
$action = $actionArray[0] . '.' . $actionArray[1];
$adminLogin = $callback[1][1];
$targetLogin = $actionArray[2];
switch ($action) {
case self::ACTION_SPECTATE_PLAYER:
try {
$this->maniaControl->getClient()->forceSpectator($adminLogin, PlayerActions::SPECTATOR_BUT_KEEP_SELECTABLE);
$this->maniaControl->getClient()->forceSpectatorTarget($adminLogin, $targetLogin, 1);
} catch (PlayerStateException $e) {
} catch (UnknownPlayerException $e) {
}
break;
case self::ACTION_OPEN_PLAYER_DETAILED:
$player = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
$this->maniaControl->getPlayerManager()->getPlayerDetailed()->showPlayerDetailed($player, $targetLogin);
unset($this->playersListShown[$player->login]);
break;
case self::ACTION_FORCE_BLUE:
$this->maniaControl->getPlayerManager()->getPlayerActions()->forcePlayerToTeam($adminLogin, $targetLogin, PlayerActions::TEAM_BLUE);
break;
case self::ACTION_FORCE_RED:
$this->maniaControl->getPlayerManager()->getPlayerActions()->forcePlayerToTeam($adminLogin, $targetLogin, PlayerActions::TEAM_RED);
break;
case self::ACTION_FORCE_SPEC:
$this->maniaControl->getPlayerManager()->getPlayerActions()->forcePlayerToSpectator($adminLogin, $targetLogin, PlayerActions::SPECTATOR_BUT_KEEP_SELECTABLE);
break;
case self::ACTION_FORCE_PLAY:
$this->maniaControl->getPlayerManager()->getPlayerActions()->forcePlayerToPlay($adminLogin, $targetLogin);
break;
case self::ACTION_MUTE_PLAYER:
$this->maniaControl->getPlayerManager()->getPlayerActions()->mutePlayer($adminLogin, $targetLogin);
$this->showPlayerList($this->maniaControl->getPlayerManager()->getPlayer($adminLogin));
break;
case self::ACTION_UNMUTE_PLAYER:
$this->maniaControl->getPlayerManager()->getPlayerActions()->unMutePlayer($adminLogin, $targetLogin);
$this->showPlayerList($this->maniaControl->getPlayerManager()->getPlayer($adminLogin));
break;
case self::ACTION_WARN_PLAYER:
$this->maniaControl->getPlayerManager()->getPlayerActions()->warnPlayer($adminLogin, $targetLogin);
break;
case self::ACTION_KICK_PLAYER:
$this->maniaControl->getPlayerManager()->getPlayerActions()->kickPlayer($adminLogin, $targetLogin);
break;
case self::ACTION_BAN_PLAYER:
$this->maniaControl->getPlayerManager()->getPlayerActions()->banPlayer($adminLogin, $targetLogin);
break;
case self::ACTION_PLAYER_ADV:
$admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
$this->advancedPlayerWidget($admin, $targetLogin);
break;
case self::ACTION_ADD_AS_MASTER:
$this->maniaControl->getPlayerManager()->getPlayerActions()->grandAuthLevel($adminLogin, $targetLogin, AuthenticationManager::AUTH_LEVEL_SUPERADMIN);
break;
case self::ACTION_ADD_AS_ADMIN:
$this->maniaControl->getPlayerManager()->getPlayerActions()->grandAuthLevel($adminLogin, $targetLogin, AuthenticationManager::AUTH_LEVEL_ADMIN);
break;
case self::ACTION_ADD_AS_MOD:
$this->maniaControl->getPlayerManager()->getPlayerActions()->grandAuthLevel($adminLogin, $targetLogin, AuthenticationManager::AUTH_LEVEL_MODERATOR);
break;
case self::ACTION_REVOKE_RIGHTS:
$this->maniaControl->getPlayerManager()->getPlayerActions()->revokeAuthLevel($adminLogin, $targetLogin);
break;
case self::ACTION_FORCE_SPEC_VOTE:
/** @var $votesPlugin CustomVotesPlugin */
$votesPlugin = $this->maniaControl->getPluginManager()->getPlugin(self::DEFAULT_CUSTOM_VOTE_PLUGIN);
$admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
$target = $this->maniaControl->getPlayerManager()->getPlayer($targetLogin);
$startMessage = $admin->getEscapedNickname() . '$s started a vote to force $<' . $target->nickname . '$> into spectator!';
$votesPlugin->defineVote('forcespec', 'Force ' . $target->getEscapedNickname() . ' Spec', true, $startMessage);
$votesPlugin->startVote($admin, 'forcespec', function ($result) use (&$votesPlugin, &$target) {
$this->maniaControl->getChat()->sendInformation('$sVote successful -> Player ' . $target->getEscapedNickname() . ' forced to Spectator!');
$votesPlugin->undefineVote('forcespec');
try {
$this->maniaControl->getClient()->forceSpectator($target->login, PlayerActions::SPECTATOR_BUT_KEEP_SELECTABLE);
$this->maniaControl->getClient()->spectatorReleasePlayerSlot($target->login);
} catch (PlayerStateException $e) {
} catch (UnknownPlayerException $e) {
}
});
break;
case self::ACTION_KICK_PLAYER_VOTE:
/** @var $votesPlugin CustomVotesPlugin */
$votesPlugin = $this->maniaControl->getPluginManager()->getPlugin(self::DEFAULT_CUSTOM_VOTE_PLUGIN);
$admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
$target = $this->maniaControl->getPlayerManager()->getPlayer($targetLogin);
$startMessage = $admin->getEscapedNickname() . '$s started a vote to kick $<' . $target->nickname . '$>!';
$votesPlugin->defineVote('kick', 'Kick ' . $target->getEscapedNickname(), true, $startMessage);
$votesPlugin->startVote($admin, 'kick', function ($result) use (&$votesPlugin, &$target) {
$this->maniaControl->getChat()->sendInformation('$sVote successful -> ' . $target->getEscapedNickname() . ' got Kicked!');
$votesPlugin->undefineVote('kick');
$message = '$39F You got kicked due to a Public Vote!$z ';
try {
$this->maniaControl->getClient()->kick($target->login, $message);
} catch (UnknownPlayerException $e) {
}
});
break;
}
}
/**
* Display the Advanced Player Window
*
* @param Player $caller
* @param string $login
*/
public function advancedPlayerWidget(Player $caller, $login) {
// Set status to target player login
$this->playersListShown[$caller->login] = $login;
// Reopen PlayerList
$this->showPlayerList($caller);
}
/**
* Reopen the widget on PlayerInfoChanged / Player Connect and Disconnect
*
* @param Player $player
*/
public function updateWidget(Player $player) {
foreach ($this->playersListShown as $login => $shown) {
if (!$shown) {
continue;
}
// Check if shown player still exists
$player = $this->maniaControl->getPlayerManager()->getPlayer($login);
if (!$player) {
unset($this->playersListShown[$login]);
continue;
}
// Reopen widget
if ($shown !== self::SHOWN_MAIN_WINDOW) {
$this->playersListShown[$login] = false;
}
$this->showPlayerList($player);
}
}
}

View File

@ -0,0 +1,581 @@
<?php
namespace ManiaControl\Players;
use ManiaControl\Admin\AdminLists;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\CallbackManager;
use ManiaControl\Callbacks\Callbacks;
use ManiaControl\Callbacks\TimerListener;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
use ManiaControl\Statistics\StatisticManager;
use ManiaControl\Utils\Formatter;
use Maniaplanet\DedicatedServer\Xmlrpc\UnknownPlayerException;
/**
* Class managing Players
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class PlayerManager implements CallbackListener, TimerListener {
/*
* Constants
*/
const CB_PLAYERCONNECT = 'PlayerManagerCallback.PlayerConnect';
const CB_PLAYERDISCONNECT = 'PlayerManagerCallback.PlayerDisconnect';
const CB_PLAYERINFOCHANGED = 'PlayerManagerCallback.PlayerInfoChanged';
const CB_SERVER_EMPTY = 'PlayerManagerCallback.ServerEmpty';
const TABLE_PLAYERS = 'mc_players';
const SETTING_JOIN_LEAVE_MESSAGES = 'Enable Join & Leave Messages';
const SETTING_JOIN_LEAVE_MESSAGES_SPECTATOR = 'Enable Join & Leave Messages for Spectators';
const STAT_JOIN_COUNT = 'Joins';
const STAT_SERVERTIME = 'Servertime';
/*
* Public properties
*/
/** @var PlayerActions $playerActions */
/** @deprecated see getPlayerActions() */
public $playerActions = null;
/** @var PlayerCommands $playerCommands */
/** @deprecated see getPlayerCommands() */
public $playerCommands = null;
/** @var PlayerDetailed $playerDetailed */
/** @deprecated see getPlayerDetailed() */
public $playerDetailed = null;
/** @var PlayerDataManager $playerDataManager */
/** @deprecated see getPlayerDataManager() */
public $playerDataManager = null;
/** @var PlayerList $playerList */
/** @deprecated see getPlayerList() */
public $playerList = null;
/** @var AdminLists $adminLists */
/** @deprecated see getAdminLists() */
public $adminLists = null;
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/** @var Player[] $players */
private $players = array();
/**
* Construct a new Player Manager
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
$this->initTables();
$this->playerCommands = new PlayerCommands($maniaControl);
$this->playerActions = new PlayerActions($maniaControl);
$this->playerDetailed = new PlayerDetailed($maniaControl);
$this->playerDataManager = new PlayerDataManager($maniaControl);
$this->playerList = new PlayerList($maniaControl);
$this->adminLists = new AdminLists($maniaControl);
// Settings
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_JOIN_LEAVE_MESSAGES, true);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_JOIN_LEAVE_MESSAGES_SPECTATOR, true);
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::ONINIT, $this, 'onInit');
$this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_PLAYERCONNECT, $this, 'playerConnect');
$this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_PLAYERDISCONNECT, $this, 'playerDisconnect');
$this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_PLAYERINFOCHANGED, $this, 'playerInfoChanged');
// Player stats
$this->maniaControl->getStatisticManager()->defineStatMetaData(self::STAT_JOIN_COUNT);
$this->maniaControl->getStatisticManager()->defineStatMetaData(self::STAT_SERVERTIME, StatisticManager::STAT_TYPE_TIME);
}
/**
* Initialize necessary database tables
*
* @return bool
*/
private function initTables() {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$playerTableQuery = "CREATE TABLE IF NOT EXISTS `" . self::TABLE_PLAYERS . "` (
`index` int(11) NOT NULL AUTO_INCREMENT,
`login` varchar(100) NOT NULL,
`nickname` varchar(150) NOT NULL,
`path` varchar(100) NOT NULL,
`authLevel` int(11) NOT NULL DEFAULT '0',
`changed` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`index`),
UNIQUE KEY `login` (`login`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Player Data' AUTO_INCREMENT=1;";
$playerTableStatement = $mysqli->prepare($playerTableQuery);
if ($mysqli->error) {
trigger_error($mysqli->error, E_USER_ERROR);
return false;
}
$playerTableStatement->execute();
if ($playerTableStatement->error) {
trigger_error($playerTableStatement->error, E_USER_ERROR);
$playerTableStatement->close();
return false;
}
$playerTableStatement->close();
return true;
}
/**
* Return the player actions
*
* @return PlayerActions
*/
public function getPlayerActions() {
return $this->playerActions;
}
/**
* Return the player commands
*
* @return PlayerCommands
*/
public function getPlayerCommands() {
return $this->playerCommands;
}
/**
* Return the player detailed
*
* @return PlayerDetailed
*/
public function getPlayerDetailed() {
return $this->playerDetailed;
}
/**
* Return the player data manager
*
* @return PlayerDataManager
*/
public function getPlayerDataManager() {
return $this->playerDataManager;
}
/**
* Return the player list
*
* @return PlayerList
*/
public function getPlayerList() {
return $this->playerList;
}
/**
* Return the admin lists
*
* @return AdminLists
*/
public function getAdminLists() {
return $this->adminLists;
}
/**
* Handle OnInit callback
*/
public function onInit() {
// Add all players
$players = $this->maniaControl->getClient()->getPlayerList(300, 0, 2);
foreach ($players as $playerItem) {
if ($playerItem->playerId <= 0) {
continue;
}
try {
$detailedPlayerInfo = $this->maniaControl->getClient()->getDetailedPlayerInfo($playerItem->login);
} catch (UnknownPlayerException $exception) {
continue;
}
// Check if the Player is in a Team, to notify if its a TeamMode or not
if ($playerItem->teamId >= 0) {
$this->maniaControl->getServer()->setTeamMode(true);
}
$player = new Player($this->maniaControl, true);
$player->setInfo($playerItem);
$player->setDetailedInfo($detailedPlayerInfo);
$player->hasJoinedGame = true;
$this->addPlayer($player);
}
}
/**
* Add a player
*
* @param Player $player
* @return bool
*/
private function addPlayer(Player $player) {
$this->savePlayer($player);
$this->players[$player->login] = $player;
return true;
}
/**
* Save player in database and fill up properties
*
* @param Player $player
* @return bool
*/
private function savePlayer(Player &$player) {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
// Save player
$playerQuery = "INSERT INTO `" . self::TABLE_PLAYERS . "` (
`login`,
`nickname`,
`path`
) VALUES (
?, ?, ?
) ON DUPLICATE KEY UPDATE
`index` = LAST_INSERT_ID(`index`),
`nickname` = VALUES(`nickname`),
`path` = VALUES(`path`);";
$playerStatement = $mysqli->prepare($playerQuery);
if ($mysqli->error) {
trigger_error($mysqli->error);
return false;
}
$playerStatement->bind_param('sss', $player->login, $player->rawNickname, $player->path);
$playerStatement->execute();
if ($playerStatement->error) {
trigger_error($playerStatement->error);
$playerStatement->close();
return false;
}
$player->index = $playerStatement->insert_id;
$playerStatement->close();
// Get Player Auth Level from DB
$playerQuery = "SELECT `authLevel` FROM `" . self::TABLE_PLAYERS . "` WHERE `index` = ?;";
$playerStatement = $mysqli->prepare($playerQuery);
if ($mysqli->error) {
trigger_error($mysqli->error);
return false;
}
$playerStatement->bind_param('i', $player->index);
$playerStatement->execute();
if ($playerStatement->error) {
trigger_error($playerStatement->error);
$playerStatement->close();
return false;
}
$playerStatement->store_result();
$playerStatement->bind_result($player->authLevel);
$playerStatement->fetch();
$playerStatement->free_result();
$playerStatement->close();
return true;
}
/**
* Handle PlayerConnect Callback
*
* @param array $callback
*/
public function playerConnect(array $callback) {
$login = $callback[1][0];
try {
$playerInfo = $this->maniaControl->getClient()->getDetailedPlayerInfo($login);
$player = new Player($this->maniaControl, true);
$player->setDetailedInfo($playerInfo);
$this->addPlayer($player);
} catch (UnknownPlayerException $e) {
}
}
/**
* Handle PlayerDisconnect callback
*
* @param array $callback
*/
public function playerDisconnect(array $callback) {
$login = $callback[1][0];
$player = $this->removePlayer($login);
if (!$player) {
return;
}
// Trigger own callbacks
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_PLAYERDISCONNECT, $player);
if ($this->getPlayerCount(false) <= 0) {
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_SERVER_EMPTY);
}
if ($player->isFakePlayer()) {
return;
}
$played = Formatter::formatTimeH(time() - $player->joinTime);
$logMessage = "Player left: {$player->login} / {$player->nickname} Playtime: {$played}";
Logger::logInfo($logMessage, true);
if (!$player->isSpectator && $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_JOIN_LEAVE_MESSAGES) && !$player->isFakePlayer()
|| $player->isSpectator && $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_JOIN_LEAVE_MESSAGES_SPECTATOR)
) {
$this->maniaControl->getChat()->sendChat('$0f0$<$fff' . $player->nickname . '$> has left the game');
}
//Destroys stored PlayerData, after all Disconnect Callbacks got Handled
$this->getPlayerDataManager()->destroyPlayerData($player);
}
/**
* Remove a Player
*
* @param string $login
* @param bool $savePlayedTime
* @return Player $player
*/
private function removePlayer($login, $savePlayedTime = true) {
if (!isset($this->players[$login])) {
return null;
}
$player = $this->players[$login];
unset($this->players[$login]);
if ($savePlayedTime) {
$this->updatePlayedTime($player);
}
return $player;
}
/**
* Update total played time of the player
*
* @param Player $player
* @return bool
*/
private function updatePlayedTime(Player $player) {
if (!$player) {
return false;
}
$playedTime = time() - $player->joinTime;
return $this->maniaControl->getStatisticManager()->insertStat(self::STAT_SERVERTIME, $player, $this->maniaControl->getServer()->index, $playedTime);
}
/**
* Get the count of all Players
*
* @param bool $withoutSpectators
* @return int
*/
public function getPlayerCount($withoutSpectators = true) {
if (!$withoutSpectators) {
return count($this->players);
}
$count = 0;
foreach ($this->players as $player) {
if (!$player->isSpectator) {
$count++;
}
}
return $count;
}
/**
* Update PlayerInfo
*
* @param array $callback
*/
public function playerInfoChanged(array $callback) {
$player = $this->getPlayer($callback[1][0]['Login']);
if (!$player) {
return;
}
$player->ladderRank = $callback[1][0]["LadderRanking"];
$player->teamId = $callback[1][0]["TeamId"];
//Check if the Player is in a Team, to notify if its a TeamMode or not
if ($player->teamId >= 0) {
$this->maniaControl->getServer()->setTeamMode(true);
}
$prevJoinState = $player->hasJoinedGame;
$player->updatePlayerFlags($callback[1][0]["Flags"]);
$player->updateSpectatorStatus($callback[1][0]["SpectatorStatus"]);
//Check if Player finished joining the game
if ($player->hasJoinedGame && !$prevJoinState) {
if (!$player->isSpectator && $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_JOIN_LEAVE_MESSAGES) && !$player->isFakePlayer()
) {
$string = array(0 => '$0f0Player', 1 => '$0f0Moderator', 2 => '$0f0Admin', 3 => '$0f0SuperAdmin', 4 => '$0f0MasterAdmin');
$chatMessage = '$0f0' . $string[$player->authLevel] . ' $<$fff' . $player->nickname . '$> Nation: $<$fff' . $player->getCountry() . '$> joined!';
$this->maniaControl->getChat()->sendChat($chatMessage);
} else if ($player->isSpectator && $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_JOIN_LEAVE_MESSAGES_SPECTATOR)) {
$string = array(0 => '$0f0Player', 1 => '$0f0Moderator', 2 => '$0f0Admin', 3 => '$0f0SuperAdmin', 4 => '$0f0MasterAdmin');
$chatMessage = '$0f0' . $string[$player->authLevel] . ' $<$fff' . $player->nickname . '$> Nation: $<$fff' . $player->getCountry() . '$> joined as Spectator!';
$this->maniaControl->getChat()->sendChat($chatMessage);
}
$this->maniaControl->getChat()->sendInformation('This server uses ManiaControl v' . ManiaControl::VERSION . '!', $player->login);
$logMessage = "Player joined: {$player->login} / {$player->nickname} Nation: " . $player->getCountry() . " IP: {$player->ipAddress}";
Logger::logInfo($logMessage, true);
// Increment the Player Join Count
$this->maniaControl->getStatisticManager()->incrementStat(self::STAT_JOIN_COUNT, $player, $this->maniaControl->getServer()->index);
// Trigger own PlayerJoined callback
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_PLAYERCONNECT, $player);
}
// Trigger own callback
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_PLAYERINFOCHANGED, $player);
}
/**
* Get a Player by login
*
* @param mixed $login
* @param bool $connectedPlayersOnly
* @return Player
*/
public function getPlayer($login, $connectedPlayersOnly = false) {
if ($login instanceof Player) {
return $login;
}
if (!isset($this->players[$login])) {
if ($connectedPlayersOnly) {
return null;
}
return $this->getPlayerFromDatabaseByLogin($login);
}
return $this->players[$login];
}
/**
* Get a Player from the database
*
* @param string $playerLogin
* @return Player
*/
private function getPlayerFromDatabaseByLogin($playerLogin) {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$query = "SELECT * FROM `" . self::TABLE_PLAYERS . "`
WHERE `login` LIKE '" . $mysqli->escape_string($playerLogin) . "';";
$result = $mysqli->query($query);
if (!$result) {
trigger_error($mysqli->error);
return null;
}
$row = $result->fetch_object();
$result->free();
if (!isset($row)) {
return null;
}
$player = new Player($this->maniaControl, false);
$player->index = $row->index;
$player->login = $row->login;
$player->rawNickname = $row->nickname;
$player->nickname = Formatter::stripDirtyCodes($player->rawNickname);
$player->path = $row->path;
$player->authLevel = $row->authLevel;
return $player;
}
/**
* Get all Players
*
* @return Player[]
*/
public function getPlayers() {
return $this->players;
}
/**
* Get the count of all spectators
*
* @return int
*/
public function getSpectatorCount() {
$count = 0;
foreach ($this->players as $player) {
if ($player->isSpectator) {
$count++;
}
}
return $count;
}
/**
* Get a Player by index
*
* @param int $index
* @param bool $connectedPlayersOnly
* @return Player
*/
public function getPlayerByIndex($index, $connectedPlayersOnly = false) {
foreach ($this->players as $player) {
if ($player->index === $index) {
return $player;
}
}
// Player is not online - Get Player from Database
if (!$connectedPlayersOnly) {
return $this->getPlayerFromDatabaseByIndex($index);
}
return null;
}
/**
* Get a Player out of the database
*
* @param int $playerIndex
* @return Player
*/
private function getPlayerFromDatabaseByIndex($playerIndex) {
if (!is_numeric($playerIndex)) {
return null;
}
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$query = "SELECT * FROM `" . self::TABLE_PLAYERS . "`
WHERE `index` = {$playerIndex};";
$result = $mysqli->query($query);
if (!$result) {
trigger_error($mysqli->error);
return null;
}
$row = $result->fetch_object();
$result->free();
if (!$row) {
return null;
}
$player = new Player($this->maniaControl, false);
$player->index = $playerIndex;
$player->login = $row->login;
$player->rawNickname = $row->nickname;
$player->nickname = Formatter::stripDirtyCodes($player->rawNickname);
$player->path = $row->path;
$player->authLevel = $row->authLevel;
return $player;
}
}

View File

@ -0,0 +1,238 @@
<?php
namespace ManiaControl\Plugins;
use FML\Controls\Frame;
use FML\Controls\Label;
use FML\Controls\Labels\Label_Button;
use FML\Controls\Labels\Label_Text;
use FML\Controls\Quads\Quad_Icons64x64_1;
use FML\Script\Features\Paging;
use FML\Script\Script;
use ManiaControl\Admin\AuthenticationManager;
use ManiaControl\Configurator\ConfiguratorMenu;
use ManiaControl\ManiaControl;
use ManiaControl\Manialinks\ManialinkPageAnswerListener;
use ManiaControl\Players\Player;
use ManiaControl\Utils\WebReader;
/**
* Configurator for installing Plugins
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class InstallMenu implements ConfiguratorMenu, ManialinkPageAnswerListener {
/*
* Constants
*/
const SETTING_PERMISSION_INSTALL_PLUGINS = 'Install Plugins';
const ACTION_PREFIX_INSTALL_PLUGIN = 'PluginInstallMenu.Install.';
const ACTION_REFRESH_LIST = 'PluginInstallMenu.RefreshList';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/**
* Create a new plugin install menu instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Permissions
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_INSTALL_PLUGINS, AuthenticationManager::AUTH_LEVEL_SUPERADMIN);
// Callbacks
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ACTION_REFRESH_LIST, $this, 'handleRefreshListAction');
}
/**
* @see \ManiaControl\Configurators\ConfiguratorMenu::getTitle()
*/
public static function getTitle() {
return 'Install Plugins';
}
/**
* @see \ManiaControl\Configurators\ConfiguratorMenu::getMenu()
*/
public function getMenu($width, $height, Script $script, Player $player) {
$paging = new Paging();
$script->addFeature($paging);
$frame = new Frame();
// Config
$pagerSize = 9.;
$entryHeight = 5.;
$posY = 0.;
$pageFrame = null;
$url = ManiaControl::URL_WEBSERVICE . 'plugins';
$response = WebReader::getUrl($url);
$dataJson = $response->getContent();
$pluginList = json_decode($dataJson);
$index = 0;
if (!is_array($pluginList)) {
// Error text
$errorFrame = $this->getErrorFrame();
$frame->add($errorFrame);
} else if (empty($pluginList)) {
// Empty text
$emptyFrame = $this->getEmptyFrame();
$frame->add($emptyFrame);
} else {
// Build plugin list
// Pagers
$pagerPrev = new Quad_Icons64x64_1();
$frame->add($pagerPrev);
$pagerPrev->setPosition($width * 0.39, $height * -0.44, 2)->setSize($pagerSize, $pagerSize)->setSubStyle($pagerPrev::SUBSTYLE_ArrowPrev);
$pagerNext = clone $pagerPrev;
$frame->add($pagerNext);
$pagerNext->setX($width * 0.45);
$pageCountLabel = new Label_Text();
$frame->add($pageCountLabel);
$pageCountLabel->setHAlign($pageCountLabel::RIGHT)->setPosition($width * 0.35, $height * -0.44, 1)->setStyle($pageCountLabel::STYLE_TextTitle1)->setTextSize(2);
$paging->addButton($pagerNext)->addButton($pagerPrev)->setLabel($pageCountLabel);
// Info tooltip
$infoTooltipLabel = new Label();
$frame->add($infoTooltipLabel);
$infoTooltipLabel->setAlign($infoTooltipLabel::LEFT, $infoTooltipLabel::TOP)->setPosition($width * -0.45, $height * -0.22)->setSize($width * 0.7, $entryHeight)->setTextSize(1)->setTranslate(true)->setVisible(false)->setAutoNewLine(true)->setMaxLines(5);
// List plugins
foreach ($pluginList as $plugin) {
if ($this->maniaControl->getPluginManager()->isPluginIdInstalled($plugin->id)
) {
// Already installed -> Skip
continue;
}
if ($index % 10 === 0) {
// New page
$pageFrame = new Frame();
$frame->add($pageFrame);
$paging->addPage($pageFrame);
$posY = $height * 0.41;
}
$pluginFrame = new Frame();
$pageFrame->add($pluginFrame);
$pluginFrame->setY($posY);
$nameLabel = new Label_Text();
$pluginFrame->add($nameLabel);
$nameLabel->setHAlign($nameLabel::LEFT)->setX($width * -0.46)->setSize($width * 0.62, $entryHeight)->setStyle($nameLabel::STYLE_TextCardSmall)->setTextSize(2)->setText($plugin->name);
$description = "Author: {$plugin->author}\nVersion: {$plugin->currentVersion->version}\nDesc: {$plugin->description}";
$nameLabel->addTooltipLabelFeature($infoTooltipLabel, $description);
if (!$this->isPluginCompatible($plugin)) {
// Incompatibility label
$infoLabel = new Label_Text();
$pluginFrame->add($infoLabel);
$infoLabel->setHAlign($infoLabel::RIGHT)->setX($width * 0.47)->setSize($width * 0.33, $entryHeight)->setTextSize(1)->setTextColor('f30');
if ($plugin->currentVersion->min_mc_version > ManiaControl::VERSION) {
$infoLabel->setText("Needs at least MC-Version '{$plugin->currentVersion->min_mc_version}'");
} else {
$infoLabel->setText("Needs at most MC-Version '{$plugin->currentVersion->max_mc_version}'");
}
} else {
// Install button
$installButton = new Label_Button();
$pluginFrame->add($installButton);
$installButton->setHAlign($installButton::RIGHT)->setX($width * 0.47)->setStyle($installButton::STYLE_CardButtonSmall)->setText('Install')->setTranslate(true)->setAction(self::ACTION_PREFIX_INSTALL_PLUGIN . $plugin->id);
}
if ($plugin->currentVersion->verified > 0) {
// Suggested quad
$suggestedQuad = new Quad_Icons64x64_1();
$pluginFrame->add($suggestedQuad);
$suggestedQuad->setPosition($width * 0.45, $entryHeight * 0.12, 2)->setSize(4, 4)->setSubStyle($suggestedQuad::SUBSTYLE_StateSuggested);
}
$posY -= $entryHeight;
$index++;
}
}
return $frame;
}
/**
* Build the Frame to display when an Error occurred
*
* @return Frame
*/
private function getErrorFrame() {
$frame = new Frame();
$infoLabel = new Label_Text();
$frame->add($infoLabel);
$infoLabel->setVAlign($infoLabel::BOTTOM)->setY(2)->setSize(100, 25)->setTextColor('f30')->setTranslate(true)->setText('An error occurred. Please try again later.');
$refreshQuad = new Quad_Icons64x64_1();
$frame->add($refreshQuad);
$refreshQuad->setY(-4)->setSize(8, 8)->setSubStyle($refreshQuad::SUBSTYLE_Refresh)->setAction(self::ACTION_REFRESH_LIST);
return $frame;
}
/**
* Build the Frame to display when no Plugins are left to install
*
* @return Frame
*/
private function getEmptyFrame() {
$frame = new Frame();
$infoLabel = new Label_Text();
$frame->add($infoLabel);
$infoLabel->setSize(100, 50)->setTextColor('0f3')->setTranslate(true)->setText('No other plugins available.');
return $frame;
}
/**
* Check if the given Plugin can be installed without Issues
*
* @param object $plugin
* @return bool
*/
private function isPluginCompatible($plugin) {
if ($plugin->currentVersion->min_mc_version > 0 && $plugin->currentVersion->min_mc_version > ManiaControl::VERSION) {
// ManiaControl needs to be updated
return false;
}
if ($plugin->currentVersion->max_mc_version > 0 && $plugin->currentVersion->max_mc_version < ManiaControl::VERSION) {
// Plugin is outdated
return false;
}
return true;
}
/**
* @see \ManiaControl\Configurators\ConfiguratorMenu::saveConfigData()
*/
public function saveConfigData(array $configData, Player $player) {
}
/**
* Handle the Refresh MLAction
*
* @param array $actionCallback
* @param Player $player
*/
public function handleRefreshListAction(array $actionCallback, Player $player) {
$this->maniaControl->getConfigurator()->showMenu($player, $this);
}
}

74
core/Plugins/Plugin.php Normal file
View File

@ -0,0 +1,74 @@
<?php
namespace ManiaControl\Plugins;
use ManiaControl\ManiaControl;
/**
* Interface for ManiaControl Plugins
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
interface Plugin {
/*
* Constants
*/
const PLUGIN_INTERFACE = __CLASS__;
/**
* Prepare the Plugin
*
* @param ManiaControl $maniaControl
*/
public static function prepare(ManiaControl $maniaControl);
/**
* Get plugin id
*
* @return int
*/
public static function getId();
/**
* Get Plugin Name
*
* @return string
*/
public static function getName();
/**
* Get Plugin Version
*
* @return string
*/
public static function getVersion();
/**
* Get Plugin Author
*
* @return string
*/
public static function getAuthor();
/**
* Get Plugin Description
*
* @return string
*/
public static function getDescription();
/**
* Load the plugin
*
* @param ManiaControl $maniaControl
* @return bool
*/
public function load(ManiaControl $maniaControl);
/**
* Unload the plugin and its Resources
*/
public function unload();
}

View File

@ -0,0 +1,482 @@
<?php
namespace ManiaControl\Plugins;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\TimerListener;
use ManiaControl\Commands\CommandListener;
use ManiaControl\Files\FileUtil;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
use ManiaControl\Manialinks\ManialinkPageAnswerListener;
use ManiaControl\Utils\ClassUtil;
/**
* Class managing Plugins
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class PluginManager {
/*
* Constants
*/
const TABLE_PLUGINS = 'mc_plugins';
const CB_PLUGIN_LOADED = 'PluginManager.PluginLoaded';
const CB_PLUGIN_UNLOADED = 'PluginManager.PluginUnloaded';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/** @var PluginMenu $pluginMenu */
private $pluginMenu = null;
/** @var InstallMenu $pluginInstallMenu */
private $pluginInstallMenu = null;
/** @var Plugin[] $activePlugins */
private $activePlugins = array();
/** @var string[] $pluginClasses */
private $pluginClasses = array();
/**
* Construct a new plugin manager instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
$this->initTables();
$this->pluginMenu = new PluginMenu($maniaControl);
$this->maniaControl->getConfigurator()->addMenu($this->pluginMenu);
$this->pluginInstallMenu = new InstallMenu($maniaControl);
$this->maniaControl->getConfigurator()->addMenu($this->pluginInstallMenu);
}
/**
* Initialize necessary database tables
*
* @return bool
*/
private function initTables() {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$pluginsTableQuery = "CREATE TABLE IF NOT EXISTS `" . self::TABLE_PLUGINS . "` (
`index` int(11) NOT NULL AUTO_INCREMENT,
`className` varchar(100) NOT NULL,
`active` tinyint(1) NOT NULL DEFAULT '0',
`changed` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`index`),
UNIQUE KEY `className` (`className`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='ManiaControl plugin status' AUTO_INCREMENT=1;";
$tableStatement = $mysqli->prepare($pluginsTableQuery);
if ($mysqli->error) {
trigger_error($mysqli->error, E_USER_ERROR);
return false;
}
$tableStatement->execute();
if ($tableStatement->error) {
trigger_error($tableStatement->error, E_USER_ERROR);
return false;
}
$tableStatement->close();
return true;
}
/**
* Get the Plugin Id if the given Class is a Plugin
*
* @param string $pluginClass
* @return int
*/
public static function getPluginId($pluginClass) {
if (self::isPluginClass($pluginClass)) {
/** @var Plugin $pluginClass */
return $pluginClass::getId();
}
return null;
}
/**
* Check if the given class implements the plugin interface
*
* @param string $pluginClass
* @return bool
*/
public static function isPluginClass($pluginClass) {
$pluginClass = ClassUtil::getClass($pluginClass);
if (!class_exists($pluginClass, false)) {
return false;
}
$interfaces = class_implements($pluginClass, false);
if (!$interfaces) {
return false;
}
if (!in_array(Plugin::PLUGIN_INTERFACE, $interfaces)) {
return false;
}
return true;
}
/**
* Deactivate the Plugin with the given Class
*
* @param string $pluginClass
* @return bool
*/
public function deactivatePlugin($pluginClass) {
$pluginClass = $this->getPluginClass($pluginClass);
if (!$pluginClass) {
return false;
}
if (!$this->isPluginActive($pluginClass)) {
return false;
}
/** @var Plugin $plugin */
$plugin = $this->activePlugins[$pluginClass];
unset($this->activePlugins[$pluginClass]);
$plugin->unload();
if ($plugin instanceof CallbackListener) {
$this->maniaControl->getCallbackManager()->unregisterCallbackListener($plugin);
$this->maniaControl->getCallbackManager()->unregisterScriptCallbackListener($plugin);
}
if ($plugin instanceof CommandListener) {
$this->maniaControl->getCommandManager()->unregisterCommandListener($plugin);
}
if ($plugin instanceof ManialinkPageAnswerListener) {
$this->maniaControl->getManialinkManager()->unregisterManialinkPageAnswerListener($plugin);
}
if ($plugin instanceof TimerListener) {
$this->maniaControl->getTimerManager()->unregisterTimerListenings($plugin);
}
$this->savePluginStatus($pluginClass, false);
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_PLUGIN_UNLOADED, $pluginClass, $plugin);
return true;
}
/**
* Get the Class of the Plugin
*
* @param mixed $pluginClass
* @return string
*/
public static function getPluginClass($pluginClass) {
$pluginClass = ClassUtil::getClass($pluginClass);
if (!self::isPluginClass($pluginClass)) {
return null;
}
return $pluginClass;
}
/**
* Check if the Plugin is currently running
*
* @param string $pluginClass
* @return bool
*/
public function isPluginActive($pluginClass) {
$pluginClass = $this->getPluginClass($pluginClass);
return isset($this->activePlugins[$pluginClass]);
}
/**
* Save Plugin Status in Database
*
* @param string $className
* @param bool $active
* @return bool
*/
private function savePluginStatus($className, $active) {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$pluginStatusQuery = "INSERT INTO `" . self::TABLE_PLUGINS . "` (
`className`,
`active`
) VALUES (
?, ?
) ON DUPLICATE KEY UPDATE
`active` = VALUES(`active`);";
$pluginStatement = $mysqli->prepare($pluginStatusQuery);
if ($mysqli->error) {
trigger_error($mysqli->error);
return false;
}
$activeInt = ($active ? 1 : 0);
$pluginStatement->bind_param('si', $className, $activeInt);
$pluginStatement->execute();
if ($pluginStatement->error) {
trigger_error($pluginStatement->error);
$pluginStatement->close();
return false;
}
$pluginStatement->close();
return true;
}
/**
* Load complete Plugins Directory and start all configured Plugins
*
* @return string[]
*/
public function loadPlugins() {
$pluginsDirectory = MANIACONTROL_PATH . 'plugins' . DIRECTORY_SEPARATOR;
$classesBefore = get_declared_classes();
$this->loadPluginFiles($pluginsDirectory);
$classesAfter = get_declared_classes();
$newPluginClasses = array();
$newClasses = array_diff($classesAfter, $classesBefore);
foreach ($newClasses as $className) {
if (!self::isPluginClass($className)) {
continue;
}
if (!self::validatePluginClass($className)) {
$message = "The plugin class '{$className}' isn't correctly implemented: You need to return a proper ID by registering it on maniacontrol.com!";
Logger::logWarning($message);
if (!DEV_MODE) {
$message = 'Fix the plugin or turn on DEV_MODE!';
$this->maniaControl->quit($message, true);
}
}
if (!$this->addPluginClass($className)) {
continue;
}
array_push($newPluginClasses, $className);
/** @var Plugin $className */
$className::prepare($this->maniaControl);
if ($this->getSavedPluginStatus($className)) {
$this->activatePlugin($className);
}
}
return $newPluginClasses;
}
/**
* Load all Plugin Files from the Directory
*
* @param string $directory
*/
public function loadPluginFiles($directory = '') {
if (!is_readable($directory) || !is_dir($directory)) {
return;
}
$pluginFiles = scandir($directory);
foreach ($pluginFiles as $pluginFile) {
if (substr($pluginFile, 0, 1) === '.') {
continue;
}
$filePath = $directory . $pluginFile;
if (is_file($filePath)) {
if (!FileUtil::isPhpFileName($pluginFile)) {
continue;
}
$success = include_once $filePath;
if (!$success) {
Logger::logError("Couldn't load file '{$filePath}'!");
}
continue;
}
$dirPath = $directory . $pluginFile;
if (is_dir($dirPath)) {
$this->loadPluginFiles($dirPath . DIRECTORY_SEPARATOR);
continue;
}
}
}
/**
* Validate that the given class is a correctly implemented plugin class
*
* @param string $pluginClass
* @return bool
*/
private static function validatePluginClass($pluginClass) {
if (!self::isPluginClass($pluginClass)) {
return false;
}
/** @var Plugin $pluginClass */
return ($pluginClass::getId() > 0);
}
/**
* Add the class to array of loaded plugin classes
*
* @param string $pluginClass
* @return bool
*/
public function addPluginClass($pluginClass) {
$pluginClass = $this->getPluginClass($pluginClass);
if (in_array($pluginClass, $this->pluginClasses)) {
return false;
}
if (!$this->isPluginClass($pluginClass)) {
return false;
}
array_push($this->pluginClasses, $pluginClass);
sort($this->pluginClasses);
return true;
}
/**
* Get plugin status from database
*
* @param string $className
* @return bool
*/
public function getSavedPluginStatus($className) {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$pluginStatusQuery = "SELECT `active` FROM `" . self::TABLE_PLUGINS . "`
WHERE `className` = ?;";
$pluginStatement = $mysqli->prepare($pluginStatusQuery);
if ($mysqli->error) {
trigger_error($mysqli->error);
return false;
}
$pluginStatement->bind_param('s', $className);
$pluginStatement->execute();
if ($pluginStatement->error) {
trigger_error($pluginStatement->error);
$pluginStatement->close();
return false;
}
$pluginStatement->store_result();
if ($pluginStatement->num_rows <= 0) {
$pluginStatement->free_result();
$pluginStatement->close();
$this->savePluginStatus($className, false);
return false;
}
$pluginStatement->bind_result($activeInt);
$pluginStatement->fetch();
$active = ($activeInt === 1);
$pluginStatement->free_result();
$pluginStatement->close();
return $active;
}
/**
* Activate and start the plugin with the given name
*
* @param string $pluginClass
* @param string $adminLogin
* @return bool
*/
public function activatePlugin($pluginClass, $adminLogin = null) {
if (!$this->isPluginClass($pluginClass)) {
return false;
}
if ($this->isPluginActive($pluginClass)) {
return false;
}
/** @var Plugin $plugin */
$plugin = new $pluginClass();
try {
$plugin->load($this->maniaControl);
} catch (\Exception $e) {
$message = "Error during Plugin Activation of '{$pluginClass}': '{$e->getMessage()}'";
$this->maniaControl->getChat()->sendError($message, $adminLogin);
Logger::logError($message);
$this->savePluginStatus($pluginClass, false);
return false;
}
$this->activePlugins[$pluginClass] = $plugin;
$this->savePluginStatus($pluginClass, true);
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_PLUGIN_LOADED, $pluginClass, $plugin);
return true;
}
/**
* Check if the Plugin with the given ID is already installed and loaded
*
* @param int $pluginId
* @return bool
*/
public function isPluginIdInstalled($pluginId) {
foreach ($this->pluginClasses as $pluginClass) {
/** @var Plugin $pluginClass */
if ($pluginClass::getId() == $pluginId) {
return true;
}
}
return false;
}
/**
* Returns a Plugin if it is activated
*
* @param string $pluginClass
* @return Plugin
*/
public function getPlugin($pluginClass) {
if ($this->isPluginActive($pluginClass)) {
return $this->activePlugins[$pluginClass];
}
return null;
}
/**
* Get all declared plugin class names
*
* @return string[]
*/
public function getPluginClasses() {
return $this->pluginClasses;
}
/**
* Get the Ids of all active Plugins
*
* @return string[]
*/
public function getActivePluginsIds() {
$pluginsIds = array();
foreach ($this->getActivePlugins() as $plugin) {
$pluginId = $plugin::getId();
if (is_numeric($pluginId)) {
array_push($pluginsIds, $pluginId);
}
}
return $pluginsIds;
}
/**
* Get all active Plugins
*
* @return Plugin[]
*/
public function getActivePlugins() {
return $this->activePlugins;
}
/**
* Fetch the Plugins List from the ManiaControl Website
*
* @param callable $function
*/
public function fetchPluginList(callable $function) {
$url = ManiaControl::URL_WEBSERVICE . 'plugins';
$this->maniaControl->getFileReader()->loadFile($url, function ($dataJson, $error) use (&$function) {
$data = json_decode($dataJson);
call_user_func($function, $data, $error);
});
}
}

429
core/Plugins/PluginMenu.php Normal file
View File

@ -0,0 +1,429 @@
<?php
namespace ManiaControl\Plugins;
use FML\Components\CheckBox;
use FML\Components\ValuePicker;
use FML\Controls\Entry;
use FML\Controls\Frame;
use FML\Controls\Label;
use FML\Controls\Labels\Label_Button;
use FML\Controls\Labels\Label_Text;
use FML\Controls\Quad;
use FML\Controls\Quads\Quad_Icons128x128_1;
use FML\Controls\Quads\Quad_Icons128x32_1;
use FML\Controls\Quads\Quad_Icons64x64_1;
use FML\Script\Features\Paging;
use FML\Script\Script;
use ManiaControl\Admin\AuthenticationManager;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\CallbackManager;
use ManiaControl\Configurator\ConfiguratorMenu;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
use ManiaControl\Manialinks\ManialinkPageAnswerListener;
use ManiaControl\Players\Player;
use ManiaControl\Settings\Setting;
/**
* Configurator for enabling and disabling Plugins
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class PluginMenu implements CallbackListener, ConfiguratorMenu, ManialinkPageAnswerListener {
/*
* Constants
*/
const ACTION_PREFIX_ENABLEPLUGIN = 'PluginMenu.Enable.';
const ACTION_PREFIX_DISABLEPLUGIN = 'PluginMenu.Disable.';
const ACTION_PREFIX_SETTINGS = 'PluginMenu.Settings.';
const ACTION_PREFIX_SETTING = 'PluginMenuSetting.';
const ACTION_BACK_TO_PLUGINS = 'PluginMenu.BackToPlugins';
const ACTION_PREFIX_UPDATEPLUGIN = 'PluginMenu.Update.';
const ACTION_UPDATEPLUGINS = 'PluginMenu.Update.All';
const SETTING_PERMISSION_CHANGE_PLUGIN_SETTINGS = 'Change Plugin Settings';
const CACHE_SETTING_CLASS = 'PluginMenuCache.SettingClass';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/**
* Construct a new plugin menu instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_PLAYERMANIALINKPAGEANSWER, $this, 'handleManialinkPageAnswer');
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ACTION_BACK_TO_PLUGINS, $this, 'backToPlugins');
// Permissions
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_CHANGE_PLUGIN_SETTINGS, AuthenticationManager::AUTH_LEVEL_SUPERADMIN);
}
/**
* @see \ManiaControl\Configurators\ConfiguratorMenu::getTitle()
*/
public static function getTitle() {
return 'Plugins';
}
/**
* Return back to the plugins overview page
*
* @param array $callback
* @param Player $player
*/
public function backToPlugins($callback, Player $player) {
$player->destroyCache($this, self::CACHE_SETTING_CLASS);
$this->maniaControl->getConfigurator()->showMenu($player, $this);
}
/**
* @see \ManiaControl\Configurators\ConfiguratorMenu::getMenu()
*/
public function getMenu($width, $height, Script $script, Player $player) {
$paging = new Paging();
$script->addFeature($paging);
$frame = new Frame();
$pluginClasses = $this->maniaControl->getPluginManager()->getPluginClasses();
// Config
$pagerSize = 9.;
$entryHeight = 5.;
$pageMaxCount = 10;
// Pagers
$pagerPrev = new Quad_Icons64x64_1();
$frame->add($pagerPrev);
$pagerPrev->setPosition($width * 0.39, $height * -0.44, 2);
$pagerPrev->setSize($pagerSize, $pagerSize);
$pagerPrev->setSubStyle(Quad_Icons64x64_1::SUBSTYLE_ArrowPrev);
$pagerNext = new Quad_Icons64x64_1();
$frame->add($pagerNext);
$pagerNext->setPosition($width * 0.45, $height * -0.44, 2);
$pagerNext->setSize($pagerSize, $pagerSize);
$pagerNext->setSubStyle(Quad_Icons64x64_1::SUBSTYLE_ArrowNext);
$paging->addButton($pagerNext);
$paging->addButton($pagerPrev);
$pageCountLabel = new Label_Text();
$frame->add($pageCountLabel);
$pageCountLabel->setHAlign($pageCountLabel::RIGHT);
$pageCountLabel->setPosition($width * 0.35, $height * -0.44, 1);
$pageCountLabel->setStyle($pageCountLabel::STYLE_TextTitle1);
$pageCountLabel->setTextSize(2);
$paging->setLabel($pageCountLabel);
$settingClass = $player->getCache($this, self::CACHE_SETTING_CLASS);
if ($settingClass) {
// Show Settings Menu
return $this->getPluginSettingsMenu($frame, $width, $height, $paging, $player, $settingClass);
}
// Display normal Plugin List
// Plugin pages
$posY = 0.;
$pluginUpdates = $this->maniaControl->getUpdateManager()->getPluginUpdateManager()->getPluginsUpdates();
usort($pluginClasses, function ($pluginClassA, $pluginClassB) {
/** @var Plugin $pluginClassA */
/** @var Plugin $pluginClassB */
return strcmp($pluginClassA::getName(), $pluginClassB::getName());
});
$pageFrame = null;
foreach ($pluginClasses as $index => $pluginClass) {
/** @var Plugin $pluginClass */
if ($index % $pageMaxCount === 0) {
$pageFrame = new Frame();
$frame->add($pageFrame);
$paging->addPage($pageFrame);
$posY = $height * 0.41;
}
$active = $this->maniaControl->getPluginManager()->isPluginActive($pluginClass);
$pluginFrame = new Frame();
$pageFrame->add($pluginFrame);
$pluginFrame->setY($posY);
$activeQuad = new Quad_Icons64x64_1();
$pluginFrame->add($activeQuad);
$activeQuad->setPosition($width * -0.45, -0.1, 1);
$activeQuad->setSize($entryHeight * 0.9, $entryHeight * 0.9);
if ($active) {
$activeQuad->setSubStyle($activeQuad::SUBSTYLE_LvlGreen);
} else {
$activeQuad->setSubStyle($activeQuad::SUBSTYLE_LvlRed);
}
$nameLabel = new Label_Text();
$pluginFrame->add($nameLabel);
$nameLabel->setHAlign($nameLabel::LEFT);
$nameLabel->setX($width * -0.4);
$nameLabel->setSize($width * 0.5, $entryHeight);
$nameLabel->setStyle($nameLabel::STYLE_TextCardSmall);
$nameLabel->setTextSize(2);
$nameLabel->setText($pluginClass::getName());
$descriptionLabel = new Label();
$pageFrame->add($descriptionLabel);
$descriptionLabel->setAlign($descriptionLabel::LEFT, $descriptionLabel::TOP);
$descriptionLabel->setPosition($width * -0.45, $height * -0.22);
$descriptionLabel->setSize($width * 0.7, $entryHeight);
$descriptionLabel->setTextSize(2);
$descriptionLabel->setTranslate(true);
$descriptionLabel->setVisible(false);
$descriptionLabel->setAutoNewLine(true);
$descriptionLabel->setMaxLines(5);
$description = "Author: {$pluginClass::getAuthor()}\nVersion: {$pluginClass::getVersion()}\nDesc: {$pluginClass::getDescription()}";
$descriptionLabel->setText($description);
$nameLabel->addTooltipFeature($descriptionLabel);
$quad = new Quad_Icons128x32_1();
$pluginFrame->add($quad);
$quad->setSubStyle($quad::SUBSTYLE_Settings);
$quad->setX(15);
$quad->setZ(1);
$quad->setSize(5, 5);
$quad->setAction(self::ACTION_PREFIX_SETTINGS . $pluginClass);
$statusChangeButton = new Label_Button();
$pluginFrame->add($statusChangeButton);
$statusChangeButton->setHAlign($statusChangeButton::RIGHT);
$statusChangeButton->setX($width * 0.45);
$statusChangeButton->setStyle($statusChangeButton::STYLE_CardButtonSmall);
if ($active) {
$statusChangeButton->setTextPrefix('$f00');
$statusChangeButton->setText('Deactivate');
$statusChangeButton->setAction(self::ACTION_PREFIX_DISABLEPLUGIN . $pluginClass);
} else {
$statusChangeButton->setTextPrefix('a');
$statusChangeButton->setText('Activate');
$statusChangeButton->setAction(self::ACTION_PREFIX_ENABLEPLUGIN . $pluginClass);
}
if ($pluginUpdates && array_key_exists($pluginClass::getId(), $pluginUpdates)) {
$quadUpdate = new Quad_Icons128x128_1();
$pluginFrame->add($quadUpdate);
$quadUpdate->setSubStyle($quadUpdate::SUBSTYLE_ProfileVehicle);
$quadUpdate->setX(56);
$quadUpdate->setZ(2);
$quadUpdate->setSize(5, 5);
$quadUpdate->setAction(self::ACTION_PREFIX_UPDATEPLUGIN . $pluginClass);
}
$posY -= $entryHeight;
}
if ($pluginUpdates) {
$updatePluginsButton = new Label_Button();
$frame->add($updatePluginsButton);
$updatePluginsButton->setHAlign($updatePluginsButton::RIGHT);
$updatePluginsButton->setPosition($width * 0.5, -29, 2);
$updatePluginsButton->setWidth(10);
$updatePluginsButton->setStyle($updatePluginsButton::STYLE_CardButtonSmallS);
$updatePluginsButton->setText(count($pluginUpdates) . ' update(s)');
$updatePluginsButton->setAction(self::ACTION_UPDATEPLUGINS);
}
return $frame;
}
/**
* Get the Frame with the Plugin Settings
*
* @param Frame $frame
* @param float $width
* @param float $height
* @param Paging $paging
* @param Player $player
* @param string $settingClass
* @return Frame
*/
private function getPluginSettingsMenu(Frame $frame, $width, $height, Paging $paging, Player $player, $settingClass) {
// TODO: centralize menu code to use by mc settings and plugin settings
$settings = $this->maniaControl->getSettingManager()->getSettingsByClass($settingClass);
$pageSettingsMaxCount = 11;
$posY = 0;
$index = 0;
$settingHeight = 5.;
$pageFrame = null;
//Headline Label
$headLabel = new Label_Text();
$frame->add($headLabel);
$headLabel->setHAlign($headLabel::LEFT);
$headLabel->setPosition($width * -0.46, $height * 0.41);
$headLabel->setSize($width * 0.6, $settingHeight);
$headLabel->setStyle($headLabel::STYLE_TextCardSmall);
$headLabel->setTextSize(3);
$headLabel->setText($settingClass);
$headLabel->setTextColor('ff0');
foreach ($settings as $setting) {
if ($index % $pageSettingsMaxCount === 0) {
$pageFrame = new Frame();
$frame->add($pageFrame);
$paging->addPage($pageFrame);
$posY = $height * 0.41 - $settingHeight * 1.5;
}
$settingFrame = new Frame();
$pageFrame->add($settingFrame);
$settingFrame->setY($posY);
$nameLabel = new Label_Text();
$settingFrame->add($nameLabel);
$nameLabel->setHAlign($nameLabel::LEFT);
$nameLabel->setX($width * -0.46);
$nameLabel->setSize($width * 0.6, $settingHeight);
$nameLabel->setStyle($nameLabel::STYLE_TextCardSmall);
$nameLabel->setTextSize(2);
$nameLabel->setText($setting->setting);
$nameLabel->setTextColor('fff');
if ($setting->type === Setting::TYPE_BOOL) {
// Boolean checkbox
$quad = new Quad();
$quad->setPosition($width * 0.33, 0, -0.01);
$quad->setSize(4, 4);
$checkBox = new CheckBox(self::ACTION_PREFIX_SETTING . $setting->index, $setting->value, $quad);
$settingFrame->add($checkBox);
} else if ($setting->type === Setting::TYPE_SET) {
// SET value picker
$label = new Label_Text();
$label->setX($width * 0.33);
$label->setSize($width * 0.3, $settingHeight * 0.9);
$label->setStyle($label::STYLE_TextValueSmall);
$label->setTextSize(1);
$valuePicker = new ValuePicker(self::ACTION_PREFIX_SETTING . $setting->index, $setting->set, $setting->value, $label);
$settingFrame->add($valuePicker);
} else {
// Value entry
$entry = new Entry();
$settingFrame->add($entry);
$entry->setX($width * 0.33);
$entry->setSize($width * 0.3, $settingHeight * 0.9);
$entry->setTextSize(1);
$entry->setStyle(Label_Text::STYLE_TextValueSmall);
$entry->setName(self::ACTION_PREFIX_SETTING . $setting->index);
$entry->setDefault($setting->value);
}
$posY -= $settingHeight;
$index++;
}
$backButton = new Label_Button();
$frame->add($backButton);
$backButton->setStyle($backButton::STYLE_CardMain_Quit);
$backButton->setHAlign($backButton::LEFT);
$backButton->setScale(0.75);
$backButton->setText('Back');
$backButton->setPosition(-$width / 2 + 7, -$height / 2 + 7);
$backButton->setAction(self::ACTION_BACK_TO_PLUGINS);
return $frame;
}
/**
* Handle PlayerManialinkPageAnswer callback
*
* @param array $callback
*/
public function handleManialinkPageAnswer(array $callback) {
$login = $callback[1][1];
$player = $this->maniaControl->getPlayerManager()->getPlayer($login);
if (!$player) {
return;
}
$actionId = $callback[1][2];
$enable = (strpos($actionId, self::ACTION_PREFIX_ENABLEPLUGIN) === 0);
$disable = (strpos($actionId, self::ACTION_PREFIX_DISABLEPLUGIN) === 0);
$settings = (strpos($actionId, self::ACTION_PREFIX_SETTINGS) === 0);
if (!$enable && !$disable && !$settings) {
return;
}
if ($enable) {
$pluginClass = substr($actionId, strlen(self::ACTION_PREFIX_ENABLEPLUGIN));
/** @var Plugin $pluginClass */
$activated = $this->maniaControl->getPluginManager()->activatePlugin($pluginClass, $player->login);
if ($activated) {
$this->maniaControl->getChat()->sendSuccess($pluginClass::getName() . ' activated!', $player);
Logger::logInfo("{$player->login} activated '{$pluginClass}'!", true);
} else {
$this->maniaControl->getChat()->sendError('Error activating ' . $pluginClass::getName() . '!', $player);
}
} else if ($disable) {
$pluginClass = substr($actionId, strlen(self::ACTION_PREFIX_DISABLEPLUGIN));
/** @var Plugin $pluginClass */
$deactivated = $this->maniaControl->getPluginManager()->deactivatePlugin($pluginClass);
if ($deactivated) {
$this->maniaControl->getChat()->sendSuccess($pluginClass::getName() . ' deactivated!', $player);
Logger::logInfo("{$player->login} deactivated '{$pluginClass}'!", true);
} else {
$this->maniaControl->getChat()->sendError('Error deactivating ' . $pluginClass::getName() . '!', $player);
}
} else if ($settings) {
// Open Settings Menu
$pluginClass = substr($actionId, strlen(self::ACTION_PREFIX_SETTINGS));
$player->setCache($this, self::CACHE_SETTING_CLASS, $pluginClass);
}
// Reopen the Menu
$this->maniaControl->getConfigurator()->showMenu($player, $this);
}
/**
* @see \ManiaControl\Configurators\ConfiguratorMenu::saveConfigData()
*/
public function saveConfigData(array $configData, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CHANGE_PLUGIN_SETTINGS)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
if (!$configData[3] || strpos($configData[3][0]['Name'], self::ACTION_PREFIX_SETTING) !== 0) {
return;
}
$prefixLength = strlen(self::ACTION_PREFIX_SETTING);
foreach ($configData[3] as $settingData) {
$settingIndex = (int)substr($settingData['Name'], $prefixLength);
$settingObject = $this->maniaControl->getSettingManager()->getSettingObjectByIndex($settingIndex);
if (!$settingObject) {
continue;
}
if (!$settingData || $settingData['Value'] == $settingObject->value) {
continue;
}
$settingObject->value = $settingData['Value'];
$this->maniaControl->getSettingManager()->saveSetting($settingObject);
}
$this->maniaControl->getChat()->sendSuccess('Plugin Settings saved!', $player);
// Reopen the Menu
$this->maniaControl->getConfigurator()->showMenu($player, $this);
}
}

459
core/Server/Commands.php Normal file
View File

@ -0,0 +1,459 @@
<?php
namespace ManiaControl\Server;
use FML\Controls\Quads\Quad_BgRaceScore2;
use FML\Controls\Quads\Quad_Icons128x32_1;
use FML\Controls\Quads\Quad_Icons64x64_1;
use ManiaControl\Admin\AuthenticationManager;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\Callbacks;
use ManiaControl\Callbacks\TimerListener;
use ManiaControl\Commands\CommandListener;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
use ManiaControl\Manialinks\ManialinkPageAnswerListener;
use ManiaControl\Players\Player;
use Maniaplanet\DedicatedServer\Xmlrpc\GameModeException;
/**
* Class offering various Commands related to the Dedicated Server
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class Commands implements CallbackListener, CommandListener, ManialinkPageAnswerListener, TimerListener {
/*
* Constants
*/
const ACTION_SET_PAUSE = 'ServerCommands.SetPause';
const ACTION_EXTEND_WARMUP = 'ServerCommands.ExtendWarmup';
const ACTION_END_WARMUP = 'ServerCommands.EndWarmup';
const ACTION_CANCEL_VOTE = 'ServerCommands.CancelVote';
const CB_VOTE_CANCELLED = 'ServerCommands.VoteCancelled';
const SETTING_PERMISSION_CANCEL_VOTE = 'Cancel Vote';
const SETTING_PERMISSION_SET_PAUSE = 'Set Pause';
const SETTING_PERMISSION_HANDLE_WARMUP = 'Handle Warmup';
const SETTING_PERMISSION_SHOW_SYSTEMINFO = 'Show SystemInfo';
const SETTING_PERMISSION_SHUTDOWN_SERVER = 'Shutdown Server';
const SETTING_PERMISSION_CHANGE_SERVERSETTINGS = 'Change ServerSettings';
const COMMAND_EXTEND_WARMUP = 'WarmUp_Extend';
const COMMAND_FORCE_WARMUP = 'Command_ForceWarmUp';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
private $serverShutdownTime = -1;
private $serverShutdownEmpty = false;
/**
* Create a new server commands instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Callbacks
$this->maniaControl->getTimerManager()->registerTimerListening($this, 'each5Seconds', 5000);
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::ONINIT, $this, 'handleOnInit');
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::WARMUPSTATUS, $this, 'handleWarmUpStatus');
// Chat commands
$this->maniaControl->getCommandManager()->registerCommandListener('setservername', $this, 'commandSetServerName', true, 'Sets the ServerName.');
$this->maniaControl->getCommandManager()->registerCommandListener('setpwd', $this, 'commandSetPwd', true, 'Sets play password.');
$this->maniaControl->getCommandManager()->registerCommandListener('setspecpwd', $this, 'commandSetSpecPwd', true, 'Sets spectator password.');
$this->maniaControl->getCommandManager()->registerCommandListener('setmaxplayers', $this, 'commandSetMaxPlayers', true, 'Sets the maximum number of players.');
$this->maniaControl->getCommandManager()->registerCommandListener('setmaxspectators', $this, 'commandSetMaxSpectators', true, 'Sets the maximum number of spectators.');
$this->maniaControl->getCommandManager()->registerCommandListener('shutdownserver', $this, 'commandShutdownServer', true, 'Shuts down the ManiaPlanet server.');
$this->maniaControl->getCommandManager()->registerCommandListener('systeminfo', $this, 'commandSystemInfo', true, 'Shows system information.');
$this->maniaControl->getCommandManager()->registerCommandListener('cancel', $this, 'commandCancelVote', true, 'Cancels the current vote.');
// Page actions
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ACTION_SET_PAUSE, $this, 'setPause');
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ACTION_EXTEND_WARMUP, $this, 'commandExtendWarmup');
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ACTION_END_WARMUP, $this, 'commandEndWarmup');
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ACTION_CANCEL_VOTE, $this, 'commandCancelVote');
}
/**
* Handle ManiaControl OnInit Callback
*/
public function handleOnInit() {
// Permissions
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_SHUTDOWN_SERVER, AuthenticationManager::AUTH_LEVEL_SUPERADMIN);
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_SHOW_SYSTEMINFO, AuthenticationManager::AUTH_LEVEL_SUPERADMIN);
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_CHANGE_SERVERSETTINGS, AuthenticationManager::AUTH_LEVEL_ADMIN);
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_SET_PAUSE, AuthenticationManager::AUTH_LEVEL_MODERATOR);
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_CANCEL_VOTE, AuthenticationManager::AUTH_LEVEL_MODERATOR);
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_HANDLE_WARMUP, AuthenticationManager::AUTH_LEVEL_MODERATOR);
$this->updateCancelVoteMenuItem();
$this->updateWarmUpMenuItems();
}
/**
* Add the cancel vote menu item
*/
private function updateCancelVoteMenuItem() {
$itemQuad = new Quad_Icons64x64_1();
$itemQuad->setSubStyle($itemQuad::SUBSTYLE_ArrowRed);
$itemQuad->setAction(self::ACTION_CANCEL_VOTE);
$this->maniaControl->getActionsMenu()->addMenuItem($itemQuad, false, 30, 'Cancel Vote');
}
/**
* Manage the WarmUp related menu items
*/
private function updateWarmUpMenuItems() {
$pauseExists = false;
try {
$scriptInfos = $this->maniaControl->getClient()->getModeScriptInfo();
foreach ($scriptInfos->commandDescs as $param) {
if ($param->name === self::COMMAND_FORCE_WARMUP) {
$pauseExists = true;
break;
}
}
$this->maniaControl->getClient()->triggerModeScriptEvent("WarmUp_GetStatus");
} catch (GameModeException $e) {
}
// Add pause menu item
if ($pauseExists) {
$itemQuad = new Quad_Icons128x32_1();
$itemQuad->setSubStyle($itemQuad::SUBSTYLE_ManiaLinkSwitch);
$itemQuad->setAction(self::ACTION_SET_PAUSE);
$this->maniaControl->getActionsMenu()->addAdminMenuItem($itemQuad, 13, 'Pause the current game');
}
}
/**
* Handle the WarmupStatus Callback, and removes or adds the Menu Items for extending / Stopping warmup
*
* @param $warmupEnabled
*/
public function handleWarmUpStatus($warmupEnabled) {
if ($warmupEnabled) {
// Extend WarmUp menu item
$itemQuad = new Quad_BgRaceScore2();
$itemQuad->setSubStyle($itemQuad::SUBSTYLE_SendScore);
$itemQuad->setAction(self::ACTION_EXTEND_WARMUP);
$this->maniaControl->getActionsMenu()->addMenuItem($itemQuad, false, 14, 'Extend Warmup');
// Stop WarmUp menu item
$itemQuad = new Quad_Icons64x64_1();
$itemQuad->setSubStyle($itemQuad::SUBSTYLE_ArrowGreen);
$itemQuad->setAction(self::ACTION_END_WARMUP);
$this->maniaControl->getActionsMenu()->addMenuItem($itemQuad, false, 15, 'End Warmup');
} else {
$this->maniaControl->getActionsMenu()->removeMenuItem(14, false);
$this->maniaControl->getActionsMenu()->removeMenuItem(15, false);
}
}
/**
* Handle //cancelvote command
*
* @param array $chatCallback
* @param Player $player
*/
public function commandCancelVote(array $chatCallback, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CANCEL_VOTE)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
if ($this->maniaControl->getClient()->cancelVote()
) {
$this->maniaControl->getChat()->sendInformation($player->getEscapedNickname() . ' cancelled the Vote!');
} else {
$this->maniaControl->getChat()->sendInformation("There's no vote running currently!", $player);
}
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_VOTE_CANCELLED, $player);
}
/**
* Extend the WarmUp
*
* @param array $callback
* @param Player $player
*/
public function commandExtendWarmup(array $callback, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_HANDLE_WARMUP)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
try {
$this->maniaControl->getClient()->triggerModeScriptEvent('WarmUp_Extend', '10');
$this->maniaControl->getChat()->sendInformation($player->getEscapedNickname() . ' extended the WarmUp by 10 seconds!');
} catch (GameModeException $e) {
}
}
/**
* End the WarmUp
*
* @param array $callback
* @param Player $player
*/
public function commandEndWarmup(array $callback, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_HANDLE_WARMUP)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
try {
$this->maniaControl->getClient()->triggerModeScriptEvent('WarmUp_Stop', '');
$this->maniaControl->getChat()->sendInformation($player->getEscapedNickname() . ' stopped the WarmUp!');
} catch (GameModeException $e) {
}
}
/**
* Pause the current game
*
* @param array $callback
* @param Player $player
*/
public function setPause(array $callback, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_SET_PAUSE)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
try {
$this->maniaControl->getClient()->sendModeScriptCommands(array('Command_ForceWarmUp' => true));
$this->maniaControl->getChat()->sendInformation($player->getEscapedNickname() . ' paused the Game!');
} catch (GameModeException $e) {
}
}
/**
* Check Stuff each 5 Seconds
*/
public function each5Seconds() {
// TODO: move empty & delayed shutdown code into server class
// Empty shutdown
if ($this->serverShutdownEmpty) {
if ($this->maniaControl->getPlayerManager()->getPlayerCount(false) <= 0
) {
$this->shutdownServer('empty');
}
}
// Delayed shutdown
if ($this->serverShutdownTime > 0) {
if (time() >= $this->serverShutdownTime) {
$this->shutdownServer('delayed');
}
}
}
/**
* Perform server shutdown
*
* @param string $login
*/
private function shutdownServer($login = '-') {
Logger::logInfo("Server shutdown requested by '{$login}'!");
$this->maniaControl->getClient()->stopServer();
}
/**
* Handle //systeminfo command
*
* @param array $chat
* @param Player $player
*/
public function commandSystemInfo(array $chat, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_SHOW_SYSTEMINFO)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$systemInfo = $this->maniaControl->getClient()->getSystemInfo();
$message = 'SystemInfo: ip=' . $systemInfo->publishedIp . ', port=' . $systemInfo->port . ', p2pPort=' . $systemInfo->p2PPort . ', title=' . $systemInfo->titleId . ', login=' . $systemInfo->serverLogin . '.';
$this->maniaControl->getChat()->sendInformation($message, $player->login);
}
/**
* Handle //shutdownserver command
*
* @param array $chat
* @param Player $player
*/
public function commandShutdownServer(array $chat, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_SHUTDOWN_SERVER)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
// Check for delayed shutdown
$params = explode(' ', $chat[1][2]);
if (count($params) >= 2) {
$param = $params[1];
if (strtolower($param) === 'empty') {
$this->serverShutdownEmpty = !$this->serverShutdownEmpty;
if ($this->serverShutdownEmpty) {
$this->maniaControl->getChat()->sendInformation("The server will shutdown as soon as it's empty!", $player);
return;
}
$this->maniaControl->getChat()->sendInformation("Empty-shutdown cancelled!", $player);
return;
}
$delay = (int)$param;
if ($delay <= 0) {
// Cancel shutdown
$this->serverShutdownTime = -1;
$this->maniaControl->getChat()->sendInformation("Delayed shutdown cancelled!", $player);
return;
}
// Trigger delayed shutdown
$this->serverShutdownTime = time() + $delay * 60.;
$this->maniaControl->getChat()->sendInformation("The server will shut down in {$delay} minutes!", $player);
return;
}
$this->shutdownServer($player->login);
}
/**
* Handle //setservername command
*
* @param array $chat
* @param Player $player
*/
public function commandSetServerName(array $chat, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CHANGE_SERVERSETTINGS)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$params = explode(' ', $chat[1][2], 2);
if (count($params) < 2) {
$this->maniaControl->getChat()->sendUsageInfo('Usage example: //setservername ManiaPlanet Server', $player);
return;
}
$serverName = $params[1];
$this->maniaControl->getClient()->setServerName($serverName);
$this->maniaControl->getChat()->sendSuccess("Server name changed to: '{$serverName}'!", $player);
}
/**
* Handle //setpwd command
*
* @param array $chatCallback
* @param Player $player
*/
public function commandSetPwd(array $chatCallback, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CHANGE_SERVERSETTINGS)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$messageParts = explode(' ', $chatCallback[1][2], 2);
$password = '';
$successMessage = 'Password removed!';
if (isset($messageParts[1])) {
$password = $messageParts[1];
$successMessage = "Password changed to: '{$password}'!";
}
$this->maniaControl->getClient()->setServerPassword($password);
$this->maniaControl->getChat()->sendSuccess($successMessage, $player);
}
/**
* Handle //setspecpwd command
*
* @param array $chatCallback
* @param Player $player
*/
public function commandSetSpecPwd(array $chatCallback, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CHANGE_SERVERSETTINGS)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$messageParts = explode(' ', $chatCallback[1][2], 2);
$password = '';
$successMessage = 'Spectator password removed!';
if (isset($messageParts[1])) {
$password = $messageParts[1];
$successMessage = "Spectator password changed to: '{$password}'!";
}
$this->maniaControl->getClient()->setServerPasswordForSpectator($password);
$this->maniaControl->getChat()->sendSuccess($successMessage, $player);
}
/**
* Handle //setmaxplayers command
*
* @param array $chatCallback
* @param Player $player
*/
public function commandSetMaxPlayers(array $chatCallback, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CHANGE_SERVERSETTINGS)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$messageParts = explode(' ', $chatCallback[1][2], 2);
if (!isset($messageParts[1])) {
$this->maniaControl->getChat()->sendUsageInfo('Usage example: //setmaxplayers 16', $player);
return;
}
$amount = $messageParts[1];
if (!is_numeric($amount)) {
$this->maniaControl->getChat()->sendUsageInfo('Usage example: //setmaxplayers 16', $player);
return;
}
$amount = (int)$amount;
if ($amount < 0) {
$amount = 0;
}
$this->maniaControl->getClient()->setMaxPlayers($amount);
$this->maniaControl->getChat()->sendSuccess("Changed max players to: {$amount}", $player);
}
/**
* Handle //setmaxspectators command
*
* @param array $chatCallback
* @param Player $player
*/
public function commandSetMaxSpectators(array $chatCallback, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CHANGE_SERVERSETTINGS)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$messageParts = explode(' ', $chatCallback[1][2], 2);
if (!isset($messageParts[1])) {
$this->maniaControl->getChat()->sendUsageInfo('Usage example: //setmaxspectators 16', $player);
return;
}
$amount = $messageParts[1];
if (!is_numeric($amount)) {
$this->maniaControl->getChat()->sendUsageInfo('Usage example: //setmaxspectators 16', $player);
return;
}
$amount = (int)$amount;
if ($amount < 0) {
$amount = 0;
}
$this->maniaControl->getClient()->setMaxSpectators($amount);
$this->maniaControl->getChat()->sendSuccess("Changed max spectators to: {$amount}", $player);
}
}

89
core/Server/Config.php Normal file
View File

@ -0,0 +1,89 @@
<?php
namespace ManiaControl\Server;
/**
* Model Class holding the Server Config
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class Config {
/*
* Public properties
*/
public $id = null;
public $host = null;
public $port = null;
public $user = null;
public $pass = null;
/**
* Create a new server config instance
*
* @param mixed $id
* @param mixed $host
* @param mixed $port
* @param mixed $user
* @param mixed $pass
*/
public function __construct($id = null, $host = null, $port = null, $user = null, $pass = null) {
$this->id = $this->extractConfigData($id);
$this->host = $this->extractConfigData($host);
$this->port = $this->extractConfigData($port);
$this->user = $this->extractConfigData($user);
$this->pass = $this->extractConfigData($pass);
}
/**
* Extract the actual Data from the given Config Param
*
* @param mixed $configParam
* @return string
*/
private function extractConfigData($configParam) {
if (is_array($configParam)) {
return (string)reset($configParam);
}
return (string)$configParam;
}
/**
* Validate the Config Data
*
* @param string $message
* @return bool
*/
public function validate(&$message = null) {
// Host
if (!$this->host) {
$message = 'Missing Host!';
return false;
}
// Port
if (!$this->port || $this->port === 'port') {
$message = 'Missing Port!';
return false;
}
// User
if (!$this->user) {
$message = 'Missing User!';
return false;
}
if (!in_array($this->user, array('SuperAdmin', 'Admin', 'User'))) {
$message = 'Invalid User!';
return false;
}
// Pass
if (!$this->pass) {
$message = 'Missing Pass!';
return false;
}
return true;
}
}

104
core/Server/Directory.php Normal file
View File

@ -0,0 +1,104 @@
<?php
namespace ManiaControl\Server;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\CallbackManager;
use ManiaControl\Files\FileUtil;
use ManiaControl\ManiaControl;
/**
* Class offering Operations for the Server Directory
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class Directory implements CallbackListener {
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/**
* Construct new server directory instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_SERVERSTOP, $this, 'handleServerStopCallback');
}
/**
* Retrieve the Maps Folder Path
*
* @return string
*/
public function getMapsFolder() {
return $this->maniaControl->getClient()->getMapsDirectory();
}
/**
* Retrieve the Skins Folder Path
*
* @return string
*/
public function getSkinsFolder() {
return $this->maniaControl->getClient()->getSkinsDirectory();
}
/**
* Handle Server Stop Callback
*/
public function handleServerStopCallback() {
$this->cleanLogsFolder();
$this->cleanCacheFolder();
}
/**
* Clean the server logs folder
*
* @return bool
*/
private function cleanLogsFolder() {
return FileUtil::cleanDirectory($this->getLogsFolder());
}
/**
* Retrieve the Logs Folder Path
*
* @return string
*/
public function getLogsFolder() {
return $this->getGameDataFolder() . '..' . DIRECTORY_SEPARATOR . 'Logs' . DIRECTORY_SEPARATOR;
}
/**
* Retrieve the Game Data Folder Path
*
* @return string
*/
public function getGameDataFolder() {
return $this->maniaControl->getClient()->gameDataDirectory();
}
/**
* @return bool
*/
private function cleanCacheFolder() {
return FileUtil::cleanDirectory($this->getCacheFolder(), 50);
}
/**
* Retrieve the Cache Folder Path
*
* @return string
*/
public function getCacheFolder() {
return $this->getGameDataFolder() . '..' . DIRECTORY_SEPARATOR . 'CommonData' . DIRECTORY_SEPARATOR . 'Cache' . DIRECTORY_SEPARATOR;
}
}

View File

@ -0,0 +1,138 @@
<?php
namespace ManiaControl\Server;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\CallbackManager;
use ManiaControl\Callbacks\Callbacks;
use ManiaControl\ManiaControl;
use ManiaControl\Maps\Map;
use Maniaplanet\DedicatedServer\Xmlrpc\GameModeException;
/**
* Class managing Rankings
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class RankingManager implements CallbackListener {
/*
* Private properties
*/
private $rankings = array();
/**
* Construct a new ranking manager instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_MODESCRIPTCALLBACK, $this, 'handleCallbacks');
$this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_MODESCRIPTCALLBACKARRAY, $this, 'handleCallbacks');
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::ONINIT, $this, 'onInit');
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::BEGINMAP, $this, 'handleBeginMap');
//TODO won message at end of the map (disable as setting) (and public announce only all %50 (setting) players)
}
/**
* Initialize the Rankings (never call this Method)
*/
public function onInit() {
try {
$this->maniaControl->getClient()->triggerModeScriptEvent('LibXmlRpc_GetRankings', '');
} catch (GameModeException $e) {
}
}
/**
* Handle stats on callbacks (never call this Method)
*
* @param array $callback
*/
public function handleCallbacks(array $callback) {
$callbackName = $callback[1][0];
//TODO not tested in TrackMania
switch ($callbackName) {
case 'updateRankings':
$this->updateRankings($callback[1][1][0]);
break;
case 'endRound':
case 'beginRound':
case 'endMap':
case 'endMap1':
$this->updateRankings($callback[1]);
break;
}
}
/**
* Clear the rankings on the Begin of a Map
*
* @param Map $map
*/
public function handleBeginMap(Map $map) {
$this->rankings = array();
}
/**
* Update Game Rankings (never call this Method)
*
* @param string $data
*/
public function updateRankings($data) {
if (!is_string($data)) {
return;
}
//TODO in legacy mode, no data is in parameter -> fetch via method getCurrentRanking
$scores = explode(';', $data);
foreach ($scores as $player) {
if (strpos($player, ':') !== false) {
$tmp = explode(':', $player);
$this->rankings[$tmp[0]] = $tmp[1];
}
}
array_multisort($this->rankings, SORT_DESC, SORT_NUMERIC);
//TODO if Local Records activated-> sort asc
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::RANKINGSUPDATED, $this->getRankings());
}
/**
* Get Rankings
*
* @return array
*/
public function getRankings() {
return $this->rankings;
}
/**
* Get the Current Leading Players (as Login Array)
*
* @return array|null
*/
public function getLeaders() {
$leaders = array();
$prev = -1;
foreach ($this->rankings as $score) {
if ($prev !== -1 && $prev < $score) {
return $leaders;
}
// FIXME: $leader doesn't exist
array_push($leaders, $leader);
$prev = $score;
}
return null;
}
public function getPlayerRanking() {
//TODO complete this
}
}

View File

@ -0,0 +1,68 @@
<?php
namespace ManiaControl\Server;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
/**
* Manager for Game Mode Script related Stuff
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class ScriptManager {
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
private $isScriptMode = null;
/**
* Construct a new script manager instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
}
/**
* Enable script callbacks
*
* @param bool $enable
* @return bool
*/
public function enableScriptCallbacks($enable = true) {
if (!$this->isScriptMode()) {
return false;
}
$scriptSettings = $this->maniaControl->getClient()->getModeScriptSettings();
if (!array_key_exists('S_UseScriptCallbacks', $scriptSettings)) {
return false;
}
$scriptSettings['S_UseScriptCallbacks'] = (bool)$enable;
$actionName = ($enable ? 'en' : 'dis');
$this->maniaControl->getClient()->setModeScriptSettings($scriptSettings);
Logger::logInfo("Script Callbacks successfully {$actionName}abled!");
return true;
}
/**
* Check whether the Server is running in Script Mode
*
* @return bool
*/
public function isScriptMode() {
if (is_null($this->isScriptMode)) {
$gameMode = $this->maniaControl->getClient()->getGameMode();
$this->isScriptMode = ($gameMode === 0);
}
return $this->isScriptMode;
}
}

459
core/Server/Server.php Normal file
View File

@ -0,0 +1,459 @@
<?php
namespace ManiaControl\Server;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\Callbacks;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
use ManiaControl\Players\Player;
use ManiaControl\Utils\CommandLineHelper;
use Maniaplanet\DedicatedServer\Xmlrpc\Exception;
/**
* Class providing access to the connected ManiaPlanet Server
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class Server implements CallbackListener {
/*
* Constants
*/
const TABLE_SERVERS = 'mc_servers';
const CB_TEAM_MODE_CHANGED = 'Server.TeamModeChanged';
/*
* Public properties
*/
/** @var Config $config */
public $config = null;
public $index = -1;
public $ip = null;
public $port = -1;
public $p2pPort = -1;
public $login = null;
public $titleId = null;
/** @var Directory $directory */
/** @deprecated see getDirectory() */
public $directory = null;
/** @var Commands $commands */
/** @deprecated see getCommands() */
public $commands = null;
/** @var UsageReporter $usageReporter */
/** @deprecated see getUsageReporter() */
public $usageReporter = null;
/** @var RankingManager $rankingManager */
/** @deprecated see getRankingManager() */
public $rankingManager = null;
/** @var ScriptManager $scriptManager */
/** @deprecated see getScriptManager() */
public $scriptManager = null;
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
private $teamMode = null;
/**
* Construct a new Server
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
$this->initTables();
$this->directory = new Directory($maniaControl);
$this->commands = new Commands($maniaControl);
$this->usageReporter = new UsageReporter($maniaControl);
$this->rankingManager = new RankingManager($maniaControl);
$this->scriptManager = new ScriptManager($maniaControl);
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::ONINIT, $this, 'onInit');
}
/**
* Initialize necessary Database Tables
*
* @return bool
*/
private function initTables() {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$query = "CREATE TABLE IF NOT EXISTS `" . self::TABLE_SERVERS . "` (
`index` int(11) NOT NULL AUTO_INCREMENT,
`login` varchar(100) NOT NULL,
PRIMARY KEY (`index`),
UNIQUE KEY `login` (`login`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Servers' AUTO_INCREMENT=1;";
$statement = $mysqli->prepare($query);
if ($mysqli->error) {
trigger_error($mysqli->error, E_USER_ERROR);
return false;
}
$statement->execute();
if ($statement->error) {
trigger_error($statement->error, E_USER_ERROR);
return false;
}
$statement->close();
return true;
}
/**
* Return the server config
*
* @return Config
*/
public function getConfig() {
return $this->config;
}
/**
* Return the server directory
*
* @return Directory
*/
public function getDirectory() {
return $this->directory;
}
/**
* Return the server commands
*
* @return Commands
*/
public function getCommands() {
return $this->commands;
}
/**
* Return the usage reporter
*
* @return UsageReporter
*/
public function getUsageReporter() {
return $this->usageReporter;
}
/**
* Return the ranking manager
*
* @return RankingManager
*/
public function getRankingManager() {
return $this->rankingManager;
}
/**
* Return the script manager
*
* @return ScriptManager
*/
public function getScriptManager() {
return $this->scriptManager;
}
/**
* Load the server configuration from the config XML
*
* @return Config
*/
public function loadConfig() {
// Server id parameter
$serverId = CommandLineHelper::getParameter('-id');
// Server xml element with given id
$serverElement = null;
if ($serverId) {
$serverElements = $this->maniaControl->getConfig()->xpath("server[@id='{$serverId}']");
if (!$serverElements) {
$this->maniaControl->quit("No Server configured with the ID '{$serverId}'!", true);
}
$serverElement = $serverElements[0];
} else {
$serverElements = $this->maniaControl->getConfig()->xpath('server');
if (!$serverElements) {
$this->maniaControl->quit('Invalid server configuration (No Server configured).', true);
}
$serverElement = $serverElements[0];
}
// Get config elements
$hostElements = $serverElement->xpath('host');
$portElements = $serverElement->xpath('port');
$userElements = $serverElement->xpath('user');
if (!$userElements) {
$userElements = $serverElement->xpath('login');
}
$passElements = $serverElement->xpath('pass');
// Create config object
$config = new Config($serverId, $hostElements, $portElements, $userElements, $passElements);
$message = null;
if (!$config->validate($message)) {
$this->maniaControl->quit("Your config file doesn't seem to be maintained properly. Please check the server configuration again! {$message}", true);
}
$this->config = $config;
return $this->config;
}
/**
* Gets all Servers from the Database
*
* @return \stdClass[]
*/
public function getAllServers() {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$query = "SELECT * FROM `" . self::TABLE_SERVERS . "`;";
$result = $mysqli->query($query);
if (!$result) {
trigger_error($mysqli->error);
return array();
}
$servers = array();
while ($row = $result->fetch_object()) {
array_push($servers, $row);
}
$result->free();
return $servers;
}
/**
* Handle OnInit Callback
*/
public function onInit() {
$this->updateProperties();
}
/**
* Refetch the Server Properties
*/
private function updateProperties() {
// System info
$systemInfo = $this->maniaControl->getClient()->getSystemInfo();
$this->ip = $systemInfo->publishedIp;
$this->port = $systemInfo->port;
$this->p2pPort = $systemInfo->p2PPort;
$this->login = $systemInfo->serverLogin;
$this->titleId = $systemInfo->titleId;
// Database index
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$query = "INSERT INTO `" . self::TABLE_SERVERS . "` (
`login`
) VALUES (
?
) ON DUPLICATE KEY UPDATE
`index` = LAST_INSERT_ID(`index`);";
$statement = $mysqli->prepare($query);
if ($mysqli->error) {
trigger_error($mysqli->error);
return;
}
$statement->bind_param('s', $this->login);
$statement->execute();
if ($statement->error) {
trigger_error($statement->error);
$statement->close();
return;
}
$this->index = $statement->insert_id;
$statement->close();
}
/**
* Get Server Player Info
*
* @return \Maniaplanet\DedicatedServer\Structures\PlayerDetailedInfo
*/
public function getInfo() {
return $this->maniaControl->getClient()->getDetailedPlayerInfo($this->login);
}
/**
* Retrieve Validation Replay for the given Player
*
* @param string $login
* @return string
*/
public function getValidationReplay($login) {
$login = Player::parseLogin($login);
try {
$replay = $this->maniaControl->getClient()->getValidationReplay($login);
} catch (Exception $e) {
// TODO temp added 19.04.2014
$this->maniaControl->getErrorHandler()->triggerDebugNotice("Exception line 330 Server.php" . $e->getMessage());
trigger_error("Couldn't get validation replay of '{$login}'. " . $e->getMessage());
return null;
}
return $replay;
}
/**
* Retrieve Ghost Replay for the given Player
*
* @param string $login
* @return string
*/
public function getGhostReplay($login) {
$dataDir = $this->getDirectory()->getGameDataFolder();
if (!$this->checkAccess($dataDir)) {
return null;
}
// Build file name
$login = Player::parseLogin($login);
$map = $this->maniaControl->getMapManager()->getCurrentMap();
$gameMode = $this->getGameMode();
$time = time();
$fileName = "GhostReplays/Ghost.{$login}.{$gameMode}.{$time}.{$map->uid}.Replay.Gbx";
// Save ghost replay
try {
$this->maniaControl->getClient()->saveBestGhostsReplay($login, $fileName);
} catch (Exception $e) {
// TODO temp added 19.04.2014
$this->maniaControl->getErrorHandler()->triggerDebugNotice("Exception line 360 Server.php" . $e->getMessage());
trigger_error("Couldn't save ghost replay. " . $e->getMessage());
return null;
}
// Load replay file
$ghostReplay = file_get_contents("{$dataDir}Replays/{$fileName}");
if (!$ghostReplay) {
trigger_error("Couldn't retrieve saved ghost replay.");
return null;
}
return $ghostReplay;
}
/**
* Check if ManiaControl has Access to the given Directory
*
* @param string $directory
* @return bool
*/
public function checkAccess($directory) {
return (is_dir($directory) && is_writable($directory));
}
/**
* Fetch the current Game Mode
*
* @param bool $stringValue
* @param int $parseValue
* @return int | string
*/
public function getGameMode($stringValue = false, $parseValue = null) {
if (is_int($parseValue)) {
$gameMode = $parseValue;
} else {
$gameMode = $this->maniaControl->getClient()->getGameMode();
}
if ($stringValue) {
switch ($gameMode) {
case 0:
return 'Script';
case 1:
return 'Rounds';
case 2:
return 'TimeAttack';
case 3:
return 'Team';
case 4:
return 'Laps';
case 5:
return 'Cup';
case 6:
return 'Stunts';
default:
return 'Unknown';
}
}
return $gameMode;
}
/**
* Wait for the Server to have the given Status
*
* @param int $statusCode
* @return bool
*/
public function waitForStatus($statusCode = 4) {
$response = $this->maniaControl->getClient()->getStatus();
// Check if server has the given status
if ($response->code === 4) {
return true;
}
// Server not yet in given status - Wait for it...
$waitBegin = time();
$maxWaitTime = 50;
$lastStatus = $response->name;
Logger::log("Waiting for server to reach status {$statusCode}...");
Logger::log("Current Status: {$lastStatus}");
while ($response->code !== 4) {
sleep(1);
$response = $this->maniaControl->getClient()->getStatus();
if ($lastStatus !== $response->name) {
Logger::log("New Status: {$response->name}");
$lastStatus = $response->name;
}
if (time() - $maxWaitTime > $waitBegin) {
// It took too long to reach the status
Logger::logError("Server couldn't reach status {$statusCode} after {$maxWaitTime} seconds! ");
return false;
}
}
return true;
}
/**
* Set whether the Server Runs a Team-Based Mode or not
*
* @param bool $teamMode
*/
public function setTeamMode($teamMode = true) {
$oldStatus = $this->teamMode;
$this->teamMode = (bool)$teamMode;
// Trigger callback
if ($oldStatus !== $this->teamMode | $oldStatus === null) {
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_TEAM_MODE_CHANGED, $teamMode);
}
}
/**
* Check if the Server Runs a Team-Based Mode
*
* @return bool
*/
public function isTeamMode() {
return $this->teamMode;
}
/**
* Build the join link
*
* @return string
*/
public function getJoinLink() {
return 'maniaplanet://#join=' . $this->login . '@' . $this->titleId;
}
/**
* Check if the Servers is empty
*
* @return bool
*/
public function isEmpty() {
return ($this->maniaControl->getPlayerManager()->getPlayerCount(false) === 0);
}
}

View File

@ -0,0 +1,379 @@
<?php
namespace ManiaControl\Server;
use FML\Components\CheckBox;
use FML\Controls\Entry;
use FML\Controls\Frame;
use FML\Controls\Labels\Label_Text;
use FML\Controls\Quad;
use FML\Controls\Quads\Quad_Icons64x64_1;
use FML\Script\Features\Paging;
use FML\Script\Script;
use ManiaControl\Admin\AuthenticationManager;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\Callbacks;
use ManiaControl\Callbacks\TimerListener;
use ManiaControl\Configurator\ConfiguratorMenu;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
use ManiaControl\Players\Player;
use Maniaplanet\DedicatedServer\Structures\ServerOptions;
use Maniaplanet\DedicatedServer\Xmlrpc\ServerOptionsException;
/**
* Class offering a Configurator Menu for Server Options
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class ServerOptionsMenu implements CallbackListener, ConfiguratorMenu, TimerListener {
/*
* Constants
*/
const CB_SERVER_OPTION_CHANGED = 'ServerOptionsMenu.OptionChanged';
const CB_SERVER_OPTIONS_CHANGED = 'ServerOptionsMenu.OptionsChanged';
const SETTING_PERMISSION_CHANGE_SERVER_OPTIONS = 'Change Server Options';
const TABLE_SERVER_OPTIONS = 'mc_server_options';
const ACTION_PREFIX_OPTION = 'ServerOptionsMenu.';
/** @deprecated */
const CB_SERVERSETTING_CHANGED = self::CB_SERVER_OPTION_CHANGED;
/** @deprecated */
const CB_SERVERSETTINGS_CHANGED = self::CB_SERVER_OPTIONS_CHANGED;
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/**
* Construct a new server options menu instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
$this->initTables();
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::ONINIT, $this, 'onInit');
$this->maniaControl->getTimerManager()->registerTimerListening($this, 'saveCurrentServerOptions', 6 * 3600 * 1000);
// Permissions
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_CHANGE_SERVER_OPTIONS, AuthenticationManager::AUTH_LEVEL_SUPERADMIN);
}
/**
* Initialize necessary database tables
*
* @return bool
*/
private function initTables() {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$query = "CREATE TABLE IF NOT EXISTS `" . self::TABLE_SERVER_OPTIONS . "` (
`index` int(11) NOT NULL AUTO_INCREMENT,
`serverIndex` int(11) NOT NULL,
`optionName` varchar(100) NOT NULL,
`optionValue` varchar(500) NOT NULL,
`changed` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`index`),
UNIQUE KEY `option` (`serverIndex`, `optionName`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Server Options' AUTO_INCREMENT=1;";
$statement = $mysqli->prepare($query);
if ($mysqli->error) {
trigger_error($mysqli->error, E_USER_ERROR);
return false;
}
$statement->execute();
if ($statement->error) {
trigger_error($statement->error, E_USER_ERROR);
return false;
}
$statement->close();
return true;
}
/**
* @see \ManiaControl\Configurators\ConfiguratorMenu::getTitle()
*/
public static function getTitle() {
return 'Server Options';
}
/**
* Save the current server options in case they have been changed by an external tool
*
* @return bool
*/
public function saveCurrentServerOptions() {
$serverOptions = $this->maniaControl->getClient()->getServerOptions();
return $this->saveServerOptions($serverOptions);
}
/**
* Save the given server options in the database
*
* @param ServerOptions $serverOptions
* @param bool $triggerCallbacks
* @return bool
*/
private function saveServerOptions(ServerOptions $serverOptions, $triggerCallbacks = false) {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$query = "INSERT INTO `" . self::TABLE_SERVER_OPTIONS . "` (
`serverIndex`,
`optionName`,
`optionValue`
) VALUES (
?, ?, ?
) ON DUPLICATE KEY UPDATE
`optionValue` = VALUES(`optionValue`);";
$statement = $mysqli->prepare($query);
if ($mysqli->error) {
trigger_error($mysqli->error);
return false;
}
$optionName = null;
$optionValue = null;
$statement->bind_param('iss', $this->maniaControl->getServer()->index, $optionName, $optionValue);
$serverOptionsArray = $serverOptions->toArray();
foreach ($serverOptionsArray as $optionName => $optionValue) {
if ($optionValue === null) {
continue;
}
$statement->execute();
if ($statement->error) {
trigger_error($statement->error);
$statement->close();
return false;
}
if ($triggerCallbacks) {
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_SERVER_OPTION_CHANGED, array(self::CB_SERVER_OPTION_CHANGED, $optionName, $optionValue));
}
}
$statement->close();
return true;
}
/**
* Handle OnInit callback
*/
public function onInit() {
$this->loadOptionsFromDatabase();
}
/**
* Load options from database
*
* @return bool
*/
public function loadOptionsFromDatabase() {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$serverIndex = $this->maniaControl->getServer()->index;
$query = "SELECT * FROM `" . self::TABLE_SERVER_OPTIONS . "`
WHERE `serverIndex` = {$serverIndex};";
$result = $mysqli->query($query);
if ($mysqli->error) {
trigger_error($mysqli->error);
return false;
}
$oldServerOptions = $this->maniaControl->getClient()->getServerOptions();
$newServerOptions = new ServerOptions();
while ($row = $result->fetch_object()) {
$optionName = lcfirst($row->optionName);
if (!property_exists($oldServerOptions, $optionName)) {
continue;
}
$newServerOptions->$optionName = $row->optionValue;
settype($newServerOptions->$optionName, gettype($oldServerOptions->$optionName));
}
$result->free();
$this->fillUpMandatoryOptions($newServerOptions, $oldServerOptions);
$loaded = false;
try {
$loaded = $this->maniaControl->getClient()->setServerOptions($newServerOptions);
} catch (ServerOptionsException $exception) {
$this->maniaControl->getChat()->sendExceptionToAdmins($exception);
}
if ($loaded) {
Logger::logInfo('Server Options successfully loaded!');
} else {
Logger::logError('Error loading Server Options!');
}
return $loaded;
}
/**
* Fill up the new server options object with the necessary options based on the old options object
*
* @param ServerOptions $newServerOptions
* @param ServerOptions $oldServerOptions
* @return ServerOptions
*/
private function fillUpMandatoryOptions(ServerOptions &$newServerOptions, ServerOptions $oldServerOptions) {
$mandatoryOptions = array('name', 'comment', 'password', 'passwordForSpectator', 'nextCallVoteTimeOut', 'callVoteRatio');
foreach ($mandatoryOptions as $optionName) {
if (!isset($newServerOptions->$optionName) && isset($oldServerOptions->$optionName)) {
$newServerOptions->$optionName = $oldServerOptions->$optionName;
}
}
return $newServerOptions;
}
/**
* @see \ManiaControl\Configurators\ConfiguratorMenu::getMenu()
*/
public function getMenu($width, $height, Script $script, Player $player) {
$paging = new Paging();
$script->addFeature($paging);
$frame = new Frame();
$serverOptions = $this->maniaControl->getClient()->getServerOptions();
$serverOptionsArray = $serverOptions->toArray();
// Config
$pagerSize = 9.;
$optionHeight = 5.;
$labelTextSize = 2;
// Pagers
$pagerPrev = new Quad_Icons64x64_1();
$frame->add($pagerPrev);
$pagerPrev->setPosition($width * 0.39, $height * -0.44, 2)->setSize($pagerSize, $pagerSize)->setSubStyle($pagerPrev::SUBSTYLE_ArrowPrev);
$pagerNext = new Quad_Icons64x64_1();
$frame->add($pagerNext);
$pagerNext->setPosition($width * 0.45, $height * -0.44, 2)->setSize($pagerSize, $pagerSize)->setSubStyle($pagerNext::SUBSTYLE_ArrowNext);
$pageCountLabel = new Label_Text();
$frame->add($pageCountLabel);
$pageCountLabel->setHAlign($pageCountLabel::RIGHT)->setPosition($width * 0.35, $height * -0.44, 1)->setStyle($pageCountLabel::STYLE_TextTitle1)->setTextSize(2);
$paging->addButton($pagerNext)->addButton($pagerPrev)->setLabel($pageCountLabel);
// Pages
$posY = 0.;
$index = 0;
$pageFrame = null;
foreach ($serverOptionsArray as $name => $value) {
// Continue on CurrentMaxPlayers...
$pos = strpos($name, 'Current'); // TODO: display 'Current...' somewhere
if ($pos !== false) {
continue;
}
if ($index % 13 === 0) {
$pageFrame = new Frame();
$frame->add($pageFrame);
$posY = $height * 0.41;
$paging->addPage($pageFrame);
}
$optionsFrame = new Frame();
$pageFrame->add($optionsFrame);
$optionsFrame->setY($posY);
$nameLabel = new Label_Text();
$optionsFrame->add($nameLabel);
$nameLabel->setHAlign($nameLabel::LEFT)->setX($width * -0.46)->setSize($width * 0.4, $optionHeight)->setStyle($nameLabel::STYLE_TextCardSmall)->setTextSize($labelTextSize)->setText($name)->setTextColor('fff');
if (is_bool($value)) {
// Boolean checkbox
$quad = new Quad();
$quad->setPosition($width * 0.23, 0, -0.01)->setSize(4, 4);
$checkBox = new CheckBox(self::ACTION_PREFIX_OPTION . $name, $value, $quad);
$optionsFrame->add($checkBox);
} else {
// Other
$entry = new Entry();
$optionsFrame->add($entry);
$entry->setStyle(Label_Text::STYLE_TextValueSmall)->setX($width * 0.23)->setTextSize(1)->setSize($width * 0.48, $optionHeight * 0.9)->setName(self::ACTION_PREFIX_OPTION . $name)->setDefault($value);
if ($name === 'Comment') {
$entry->setSize($width * 0.48, $optionHeight * 3. + $optionHeight * 0.9)->setAutoNewLine(true);
$optionsFrame->setY($posY - $optionHeight * 1.5);
$posY -= $optionHeight * 3.;
$index += 3;
}
}
$posY -= $optionHeight;
$index++;
}
return $frame;
}
/**
* @see \ManiaControl\Configurators\ConfiguratorMenu::saveConfigData()
*/
public function saveConfigData(array $configData, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CHANGE_SERVER_OPTIONS)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
if (!$configData[3] || strpos($configData[3][0]['Name'], self::ACTION_PREFIX_OPTION) !== 0) {
return;
}
$prefixLength = strlen(self::ACTION_PREFIX_OPTION);
$oldServerOptions = $this->maniaControl->getClient()->getServerOptions();
$newServerOptions = new ServerOptions();
foreach ($configData[3] as $option) {
$optionName = lcfirst(substr($option['Name'], $prefixLength));
$newServerOptions->$optionName = $option['Value'];
settype($newServerOptions->$optionName, gettype($oldServerOptions->$optionName));
}
$this->fillUpMandatoryOptions($newServerOptions, $oldServerOptions);
$success = $this->applyNewServerOptions($newServerOptions, $player);
if ($success) {
$this->maniaControl->getChat()->sendSuccess('Server Options saved!', $player);
} else {
$this->maniaControl->getChat()->sendError('Server Options saving failed!', $player);
}
// Reopen the Menu
$this->maniaControl->getConfigurator()->showMenu($player, $this);
}
/**
* Apply the array of new Server Options
*
* @param ServerOptions $newServerOptions
* @param Player $player
* @return bool
*/
private function applyNewServerOptions(ServerOptions $newServerOptions, Player $player) {
try {
$this->maniaControl->getClient()->setServerOptions($newServerOptions);
} catch (ServerOptionsException $exception) {
$this->maniaControl->getChat()->sendException($exception, $player);
return false;
}
$this->saveServerOptions($newServerOptions, true);
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_SERVER_OPTIONS_CHANGED, array(self::CB_SERVER_OPTIONS_CHANGED));
return true;
}
}

View File

@ -0,0 +1,89 @@
<?php
namespace ManiaControl\Server;
use ManiaControl\Callbacks\TimerListener;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
use ManiaControl\Utils\Formatter;
use Maniaplanet\DedicatedServer\Xmlrpc\GameModeException;
/**
* Class reporting ManiaControl Usage for the Server
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class UsageReporter implements TimerListener {
/*
* Constants
*/
const UPDATE_MINUTE_COUNT = 10;
const SETTING_REPORT_USAGE = 'Report Usage to $lManiaControl.com$l';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/**
* Create a new Usage Reporter Instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_REPORT_USAGE, true);
$this->maniaControl->getTimerManager()->registerTimerListening($this, 'reportUsage', 1000 * 60 * self::UPDATE_MINUTE_COUNT);
}
/**
* Report Usage of ManiaControl on the current Server
*/
public function reportUsage() {
if (DEV_MODE
|| !$this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_REPORT_USAGE)
) {
return;
}
$properties = array();
$properties['ManiaControlVersion'] = ManiaControl::VERSION;
$properties['OperatingSystem'] = php_uname();
$properties['PHPVersion'] = phpversion();
$properties['ServerLogin'] = $this->maniaControl->getServer()->login;
$properties['TitleId'] = $this->maniaControl->getServer()->titleId;
$properties['ServerName'] = Formatter::stripDirtyCodes($this->maniaControl->getClient()->getServerName());
$properties['UpdateChannel'] = $this->maniaControl->getUpdateManager()->getCurrentUpdateChannelSetting();
$properties['PlayerCount'] = $this->maniaControl->getPlayerManager()->getPlayerCount();
$properties['MemoryUsage'] = memory_get_usage();
$properties['MemoryPeakUsage'] = memory_get_peak_usage();
$maxPlayers = $this->maniaControl->getClient()->getMaxPlayers();
$properties['MaxPlayers'] = $maxPlayers['CurrentValue'];
try {
$scriptName = $this->maniaControl->getClient()->getScriptName();
$properties['ScriptName'] = $scriptName['CurrentValue'];
} catch (GameModeException $e) {
$properties['ScriptName'] = '';
}
$properties['ActivePlugins'] = $this->maniaControl->getPluginManager()->getActivePluginsIds();
$usageReport = json_encode($properties);
$url = ManiaControl::URL_WEBSERVICE . 'usagereport';
$this->maniaControl->getFileReader()->postData($url, function ($response, $error) {
$response = json_decode($response);
if ($error || !$response) {
Logger::logError('Error while Sending data: ' . print_r($error, true));
}
}, $usageReport);
}
}

View File

@ -0,0 +1,177 @@
<?php
namespace ManiaControl\Server;
use FML\Controls\Entry;
use FML\Controls\Frame;
use FML\Controls\Labels\Label_Text;
use FML\Script\Script;
use ManiaControl\Admin\AuthenticationManager;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\TimerListener;
use ManiaControl\Configurator\ConfiguratorMenu;
use ManiaControl\ManiaControl;
use ManiaControl\Players\Player;
use Maniaplanet\DedicatedServer\Structures\VoteRatio;
/**
* Class offering a Configurator Menu for Vote Ratios
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class VoteRatiosMenu implements CallbackListener, ConfiguratorMenu, TimerListener {
/*
* Constants
*/
const SETTING_PERMISSION_CHANGE_VOTE_RATIOS = 'Change Vote Ratios';
const ACTION_PREFIX_VOTE_RATIO = 'VoteRatiosMenu.VoteRatio.';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/**
* Construct a new vote ratios menu instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Permissions
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_CHANGE_VOTE_RATIOS, AuthenticationManager::AUTH_LEVEL_ADMIN);
}
/**
* @see \ManiaControl\Configurators\ConfiguratorMenu::getTitle()
*/
public static function getTitle() {
return 'Vote Ratios';
}
/**
* @see \ManiaControl\Configurators\ConfiguratorMenu::getMenu()
*/
public function getMenu($width, $height, Script $script, Player $player) {
$frame = new Frame();
$posY = $height * 0.41;
$lineHeight = 5.;
$index = 0;
$voteRatioCommands = $this->getVoteCommands();
$voteRatios = $this->maniaControl->getClient()->getCallVoteRatios();
foreach ($voteRatioCommands as $voteRatioCommand => $voteRatioDescription) {
$voteRatioFrame = new Frame();
$frame->add($voteRatioFrame);
$voteRatioFrame->setY($posY);
$nameLabel = new Label_Text();
$voteRatioFrame->add($nameLabel);
$nameLabel->setHAlign($nameLabel::LEFT)->setX($width * -0.46)->setSize($width * 0.7, $lineHeight)->setTextSize(2)->setTranslate(true)->setText($voteRatioDescription);
$entry = new Entry();
$voteRatioFrame->add($entry);
$entry->setX($width * 0.35)->setSize($width * 0.14, $lineHeight * 0.9)->setStyle(Label_Text::STYLE_TextValueSmall)->setTextSize($index === 0 ? 2 : 1)->setName(self::ACTION_PREFIX_VOTE_RATIO . $voteRatioCommand);
$voteRatio = $this->getVoteRatioForCommand($voteRatios, $voteRatioCommand);
if ($voteRatio) {
$entry->setDefault($voteRatio->ratio);
}
$posY -= $lineHeight;
if ($index === 0) {
$posY -= $lineHeight;
}
$index++;
}
return $frame;
}
/**
* Get the list of available vote commands
*
* @return string[]
*/
private function getVoteCommands() {
return array(VoteRatio::COMMAND_DEFAULT => 'Default', VoteRatio::COMMAND_RESTART_MAP => 'Restart Map', VoteRatio::COMMAND_NEXT_MAP => 'Skip Map', VoteRatio::COMMAND_SET_NEXT_MAP => 'Set next Map', VoteRatio::COMMAND_JUMP_MAP => 'Jump to Map', VoteRatio::COMMAND_TEAM_BALANCE => 'Balance Teams', VoteRatio::COMMAND_SCRIPT_SETTINGS => 'Change Script Settings and Commands', VoteRatio::COMMAND_KICK => 'Kick Players', VoteRatio::COMMAND_BAN => 'Ban Players');
}
/**
* Return the vote ratio for the given command
*
* @param VoteRatio[] $voteRatios
* @param string $command
* @return VoteRatio
*/
private function getVoteRatioForCommand(array $voteRatios, $command) {
foreach ($voteRatios as $voteRatio) {
if ($voteRatio->command === $command) {
return $voteRatio;
}
}
return null;
}
/**
* @see \ManiaControl\Configurators\ConfiguratorMenu::saveConfigData()
*/
public function saveConfigData(array $configData, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CHANGE_VOTE_RATIOS)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
if (!$configData[3] || strpos($configData[3][0]['Name'], self::ACTION_PREFIX_VOTE_RATIO) !== 0) {
return;
}
$prefixLength = strlen(self::ACTION_PREFIX_VOTE_RATIO);
$newVoteRatios = array();
foreach ($configData[3] as $voteRatioEntry) {
$voteRatioName = substr($voteRatioEntry['Name'], $prefixLength);
$voteRatioValue = $voteRatioEntry['Value'];
if ($voteRatioValue === '') {
continue;
}
if (!is_numeric($voteRatioValue)) {
$this->sendInvalidValueError($player, $voteRatioName);
continue;
}
$voteRatio = new VoteRatio();
$voteRatio->command = $voteRatioName;
$voteRatio->ratio = (float)$voteRatioValue;
if (!$voteRatio->isValid()) {
$this->sendInvalidValueError($player, $voteRatioName);
continue;
}
array_push($newVoteRatios, $voteRatio);
}
$success = $this->maniaControl->getClient()->setCallVoteRatios($newVoteRatios);
if ($success) {
$this->maniaControl->getChat()->sendSuccess('Vote Ratios saved!', $player);
} else {
$this->maniaControl->getChat()->sendError('Vote Ratios saving failed!', $player);
}
// Reopen the Menu
$this->maniaControl->getConfigurator()->showMenu($player, $this);
}
/**
* Inform the player that his entered value is invalid
*
* @param Player $player
* @param string $commandName
*/
private function sendInvalidValueError(Player $player, $commandName) {
$this->maniaControl->getChat()->sendError("Invalid Value given for '{$commandName}'!", $player);
}
}

195
core/Settings/Setting.php Normal file
View File

@ -0,0 +1,195 @@
<?php
namespace ManiaControl\Settings;
use ManiaControl\Utils\ClassUtil;
/**
* ManiaControl Setting Model Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class Setting {
/*
* Constants
*/
const CLASS_NAME = __CLASS__;
const TYPE_STRING = 'string';
const TYPE_INT = 'int';
const TYPE_REAL = 'real';
const TYPE_BOOL = 'bool';
const TYPE_SET = 'set';
const VALUE_DELIMITER = ';;';
/*
* Public properties
*/
public $index = null;
public $class = null;
public $setting = null;
public $type = null;
public $value = null;
public $default = null;
public $set = null;
public $fetchTime = null;
/**
* Construct a new setting instance
*
* @param mixed $object
* @param string $settingName
* @param mixed $defaultValue
*/
public function __construct($object, $settingName, $defaultValue) {
if ($object === false) {
// Fetched from Database
$this->value = $this->castValue($this->value);
$this->default = $this->castValue($this->default);
if ($this->set) {
$this->set = $this->castValue($this->set, $this->type);
}
$this->fetchTime = time();
} else {
// Created by Values
$this->class = ClassUtil::getClass($object);
$this->setting = (string)$settingName;
$this->type = self::getValueType($defaultValue);
if ($this->type === self::TYPE_SET) {
// Save Set and use first Value as Default
$this->set = $defaultValue;
$this->value = reset($defaultValue);
} else {
$this->value = $defaultValue;
}
$this->default = $this->value;
}
}
/**
* Cast the Value based on the Setting Type
*
* @param mixed $value
* @param string $type
* @return mixed
*/
private static function castValue($value, $type = null) {
if ($type === null) {
$type = self::getValueType($value);
}
if ($type === self::TYPE_INT) {
return (int)$value;
}
if ($type === self::TYPE_REAL) {
return (float)$value;
}
if ($type === self::TYPE_BOOL) {
return (bool)$value;
}
if ($type === self::TYPE_STRING) {
return (string)$value;
}
if ($type === self::TYPE_SET) {
return explode(self::VALUE_DELIMITER, $value);
}
trigger_error("Unsupported Setting Value Type: '" . print_r($type, true) . "'!");
return $value;
}
/**
* Get Type of a Value Parameter
*
* @param mixed $value
* @return string
*/
private static function getValueType($value) {
if (is_int($value)) {
return self::TYPE_INT;
}
if (is_real($value)) {
return self::TYPE_REAL;
}
if (is_bool($value)) {
return self::TYPE_BOOL;
}
if (is_string($value)) {
return self::TYPE_STRING;
}
if (is_array($value)) {
return self::TYPE_SET;
}
trigger_error("Unsupported Setting Value Type: '" . print_r($value, true) . "'!");
return null;
}
/**
* Get whether the Setting has been persisted at some point
*
* @return bool
*/
public function isPersisted() {
return ($this->index > 0);
}
/**
* Get the Formatted Value of the Setting
*
* @return string
*/
public function getFormattedValue() {
return self::formatValue($this->value);
}
/**
* Format the given Value based on the Type
*
* @param mixed $value
* @param string $type
* @return string
*/
private static function formatValue($value, $type = null) {
if ($type === null) {
$type = self::getValueType($value);
}
if ($type === self::TYPE_BOOL) {
return ($value ? 1 : 0);
}
if ($type === self::TYPE_SET) {
return implode(self::VALUE_DELIMITER, $value);
}
return $value;
}
/**
* Get the Formatted Default of the Setting
*
* @return string
*/
public function getFormattedDefault() {
return self::formatValue($this->default);
}
/**
* Get the Formatted Set of the Setting
*
* @return string
*/
public function getFormattedSet() {
if ($this->type === self::TYPE_SET) {
return self::formatValue($this->set);
}
return '';
}
/**
* Check if the Settings belongs to the given Class
*
* @param mixed $object
* @return bool
*/
public function belongsToClass($object) {
$className = ClassUtil::getClass($object);
return ($className === $this->class);
}
}

View File

@ -0,0 +1,488 @@
<?php
namespace ManiaControl\Settings;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\Callbacks;
use ManiaControl\ManiaControl;
use ManiaControl\Plugins\PluginManager;
use ManiaControl\Utils\ClassUtil;
/**
* Class managing ManiaControl Settings and Configurations
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class SettingManager implements CallbackListener {
/*
* Constants
*/
const TABLE_SETTINGS = 'mc_settings';
const CB_SETTING_CHANGED = 'SettingManager.SettingChanged';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/** @var Setting[] $storedSettings */
private $storedSettings = array();
/**
* Construct a new setting manager instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
$this->initTables();
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::AFTERINIT, $this, 'handleAfterInit');
}
/**
* Initialize the necessary database tables
*
* @return bool
*/
private function initTables() {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$settingTableQuery = "CREATE TABLE IF NOT EXISTS `" . self::TABLE_SETTINGS . "` (
`index` INT(11) NOT NULL AUTO_INCREMENT,
`class` VARCHAR(100) NOT NULL,
`setting` VARCHAR(150) NOT NULL,
`type` VARCHAR(50) NOT NULL,
`value` VARCHAR(100) NOT NULL,
`default` VARCHAR(100) NOT NULL,
`set` VARCHAR(100) NOT NULL,
`changed` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`index`),
UNIQUE KEY `settingId` (`class`,`setting`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Settings and Configurations' AUTO_INCREMENT=1;";
$result = $mysqli->query($settingTableQuery);
if ($mysqli->error) {
trigger_error($mysqli->error, E_USER_ERROR);
}
return $result;
}
/**
* Handle After Init Callback
*/
public function handleAfterInit() {
$this->deleteUnusedSettings();
}
/**
* Delete all unused Settings that haven't been initialized during the current Startup
*
* @return bool
*/
private function deleteUnusedSettings() {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$settingQuery = "DELETE FROM `" . self::TABLE_SETTINGS . "`
WHERE `changed` < NOW() - INTERVAL 1 HOUR;";
$result = $mysqli->query($settingQuery);
if ($mysqli->error) {
trigger_error($mysqli->error);
return false;
}
if ($result) {
$this->clearStorage();
return true;
}
return false;
}
/**
* Clear the Settings Storage
*/
public function clearStorage() {
$this->storedSettings = array();
}
/**
* @deprecated
* @see SettingManager::getSettingValueByIndex()
*/
public function getSettingByIndex($settingIndex, $defaultValue = null) {
return $this->getSettingValueByIndex($settingIndex, $defaultValue);
}
/**
* Get a Setting Value by its Index
*
* @param int $settingIndex
* @param mixed $defaultValue
* @return mixed
*/
public function getSettingValueByIndex($settingIndex, $defaultValue = null) {
$setting = $this->getSettingObjectByIndex($settingIndex);
if (!$setting) {
return $defaultValue;
}
return $setting->value;
}
/**
* Get a Setting Object by its Index
*
* @param int $settingIndex
* @return Setting
*/
public function getSettingObjectByIndex($settingIndex) {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$settingQuery = "SELECT * FROM `" . self::TABLE_SETTINGS . "`
WHERE `index` = {$settingIndex};";
$result = $mysqli->query($settingQuery);
if ($mysqli->error) {
trigger_error($mysqli->error);
return null;
}
if ($result->num_rows <= 0) {
$result->free();
return null;
}
/** @var Setting $setting */
$setting = $result->fetch_object(Setting::CLASS_NAME, array(false, null, null));
$result->free();
$this->storeSetting($setting);
return $setting;
}
/**
* Store the given Setting
*
* @param Setting $setting
*/
private function storeSetting(Setting $setting) {
$this->storedSettings[$setting->class . $setting->setting] = $setting;
}
/**
* Set a Setting for the given Object
*
* @param mixed $object
* @param string $settingName
* @param mixed $value
* @return bool
*/
public function setSetting($object, $settingName, $value) {
//TODO nowhere used, everywhere saveSettings used, is it depreciated?
$setting = $this->getSettingObject($object, $settingName);
if ($setting) {
$setting->value = $value;
$saved = $this->saveSetting($setting);
if (!$saved) {
return false;
}
} else {
$saved = $this->initSetting($object, $settingName, $value);
if (!$saved) {
return false;
}
$setting = $this->getSettingObject($object, $settingName, $value);
}
$this->storeSetting($setting);
return true;
}
/**
* Get Setting by Name for the given Object
*
* @param mixed $object
* @param string $settingName
* @param mixed $default
* @return Setting
*/
public function getSettingObject($object, $settingName, $default = null) {
$settingClass = ClassUtil::getClass($object);
// Retrieve from Storage if possible
$storedSetting = $this->getStoredSetting($object, $settingName);
if ($storedSetting) {
return $storedSetting;
}
// Fetch setting
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$settingQuery = "SELECT * FROM `" . self::TABLE_SETTINGS . "`
WHERE `class` = '" . $mysqli->escape_string($settingClass) . "'
AND `setting` = '" . $mysqli->escape_string($settingName) . "';";
$result = $mysqli->query($settingQuery);
if ($mysqli->error) {
trigger_error($mysqli->error);
return null;
}
if ($result->num_rows <= 0) {
$result->free();
if ($default === null) {
return null;
}
$saved = $this->initSetting($object, $settingName, $default);
if ($saved) {
return $this->getSettingObject($object, $settingName, $default);
} else {
return null;
}
}
/** @var Setting $setting */
$setting = $result->fetch_object(Setting::CLASS_NAME, array(false, null, null));
$result->free();
$this->storeSetting($setting);
return $setting;
}
/**
* Retrieve a stored Setting
*
* @param mixed $settingClass
* @param string $settingName
* @return Setting
*/
private function getStoredSetting($settingClass, $settingName) {
$settingClass = ClassUtil::getClass($settingClass);
if (isset($this->storedSettings[$settingClass . $settingName])) {
return $this->storedSettings[$settingClass . $settingName];
}
return null;
}
/**
* Initialize a Setting for the given Object
*
* @param mixed $object
* @param string $settingName
* @param mixed $defaultValue
* @return bool
*/
public function initSetting($object, $settingName, $defaultValue) {
$setting = new Setting($object, $settingName, $defaultValue);
return $this->saveSetting($setting, true);
}
/**
* Save the given Setting in the Database
*
* @param Setting $setting
* @param bool $init
* @return bool
*/
public function saveSetting(Setting $setting, $init = false) {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
if ($init) {
// Init - Keep old value if the default didn't change
$valueUpdateString = '`value` = IF(`default` = VALUES(`default`), `value`, VALUES(`default`))';
} else {
// Set - Update value in any case
$valueUpdateString = '`value` = VALUES(`value`)';
}
$settingQuery = "INSERT INTO `" . self::TABLE_SETTINGS . "` (
`class`,
`setting`,
`type`,
`value`,
`default`,
`set`
) VALUES (
?, ?, ?, ?, ?, ?
) ON DUPLICATE KEY UPDATE
`index` = LAST_INSERT_ID(`index`),
`type` = VALUES(`type`),
{$valueUpdateString},
`default` = VALUES(`default`),
`set` = VALUES(`set`),
`changed` = NOW();";
$settingStatement = $mysqli->prepare($settingQuery);
if ($mysqli->error) {
trigger_error($mysqli->error);
return false;
}
$formattedValue = $setting->getFormattedValue();
$formattedDefault = $setting->getFormattedDefault();
$formattedSet = $setting->getFormattedSet();
$settingStatement->bind_param('ssssss', $setting->class, $setting->setting, $setting->type, $formattedValue, $formattedDefault, $formattedSet);
$settingStatement->execute();
if ($settingStatement->error) {
trigger_error($settingStatement->error);
$settingStatement->close();
return false;
}
$settingStatement->close();
// Trigger Settings Changed Callback
if (!$init) {
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_SETTING_CHANGED, $setting);
}
return true;
}
/**
* @deprecated
* @see SettingManager::getSettingValue()
*/
public function getSetting($object, $settingName, $default = null) {
return $this->getSettingValue($object, $settingName, $default);
}
/**
* Get the Setting Value directly
*
* @param mixed $object
* @param string $settingName
* @param mixed $default
* @return mixed
*/
public function getSettingValue($object, $settingName, $default = null) {
$setting = $this->getSettingObject($object, $settingName, $default);
if ($setting) {
return $setting->value;
}
return null;
}
/**
* Reset a Setting to its Default Value
*
* @param mixed $object
* @param string $settingName
* @return bool
*/
public function resetSetting($object, $settingName = null) {
if ($object instanceof Setting) {
$className = $object->class;
$settingName = $object->setting;
} else {
$className = ClassUtil::getClass($object);
}
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$settingQuery = "UPDATE `" . self::TABLE_SETTINGS . "`
SET `value` = `default`
WHERE `class` = '" . $mysqli->escape_string($className) . "'
AND `setting` = '" . $mysqli->escape_string($settingName) . "';";
$result = $mysqli->query($settingQuery);
if ($mysqli->error) {
trigger_error($mysqli->error);
return false;
}
if (isset($this->storedSettings[$className . $settingName])) {
unset($this->storedSettings[$className . $settingName]);
}
return $result;
}
/**
* Delete a Setting
*
* @param mixed $object
* @param string $settingName
* @return bool
*/
public function deleteSetting($object, $settingName = null) {
if ($object instanceof Setting) {
$className = $object->class;
$settingName = $object->setting;
} else {
$className = ClassUtil::getClass($object);
}
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$settingQuery = "DELETE FROM `" . self::TABLE_SETTINGS . "`
WHERE `class` = '" . $mysqli->escape_string($className) . "'
AND `setting` = '" . $mysqli->escape_string($settingName) . "';";
$result = $mysqli->query($settingQuery);
if ($mysqli->error) {
trigger_error($mysqli->error);
return false;
}
if (isset($this->storedSettings[$className . $settingName])) {
unset($this->storedSettings[$className . $settingName]);
}
return $result;
}
/**
* Get all Settings for the given Class
*
* @param mixed $object
* @return Setting[]
*/
public function getSettingsByClass($object) {
$className = ClassUtil::getClass($object);
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$query = "SELECT * FROM `" . self::TABLE_SETTINGS . "`
WHERE `class` = '" . $mysqli->escape_string($className) . "'
ORDER BY `setting` ASC;";
$result = $mysqli->query($query);
if ($mysqli->error) {
trigger_error($mysqli->error);
return null;
}
$settings = array();
while ($setting = $result->fetch_object(Setting::CLASS_NAME, array(false, null, null))) {
$settings[$setting->index] = $setting;
}
$result->free();
return $settings;
}
/**
* Get all Settings
*
* @return Setting[]
*/
public function getSettings() {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$query = "SELECT * FROM `" . self::TABLE_SETTINGS . "`
ORDER BY `class` ASC, `setting` ASC;";
$result = $mysqli->query($query);
if ($mysqli->error) {
trigger_error($mysqli->error);
return null;
}
$settings = array();
while ($setting = $result->fetch_object(Setting::CLASS_NAME, array(false, null, null))) {
$settings[$setting->index] = $setting;
}
$result->free();
return $settings;
}
/**
* Get all Setting Classes
*
* @param bool $hidePluginClasses
* @return string[]
*/
public function getSettingClasses($hidePluginClasses = false) {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$query = "SELECT DISTINCT `class` FROM `" . self::TABLE_SETTINGS . "`
ORDER BY `class` ASC;";
$result = $mysqli->query($query);
if ($mysqli->error) {
trigger_error($mysqli->error);
return null;
}
$settingClasses = array();
while ($row = $result->fetch_object()) {
if (!$hidePluginClasses || !PluginManager::isPluginClass($row->class)) {
array_push($settingClasses, $row->class);
}
}
$result->free();
return $settingClasses;
}
}

View File

@ -0,0 +1,307 @@
<?php
namespace ManiaControl\Statistics;
use FML\Controls\Frame;
use FML\Controls\Label;
use FML\Controls\Labels\Label_Text;
use FML\Controls\Quad;
use FML\Controls\Quads\Quad_BgsPlayerCard;
use FML\Controls\Quads\Quad_Icons64x64_1;
use FML\Controls\Quads\Quad_UIConstruction_Buttons;
use FML\ManiaLink;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\CallbackManager;
use ManiaControl\Callbacks\Callbacks;
use ManiaControl\Commands\CommandListener;
use ManiaControl\ManiaControl;
use ManiaControl\Manialinks\ManialinkManager;
use ManiaControl\Manialinks\ManialinkPageAnswerListener;
use ManiaControl\Players\Player;
use ManiaControl\Players\PlayerManager;
use ManiaControl\Utils\Formatter;
/**
* Simple Stats List Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class SimpleStatsList implements ManialinkPageAnswerListener, CallbackListener, CommandListener {
/*
* Constants
*/
const ACTION_OPEN_STATSLIST = 'SimpleStatsList.OpenStatsList';
const ACTION_SORT_STATS = 'SimpleStatsList.SortStats';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
private $statArray = array();
private $statsWidth = 0;
/**
* Construct a new simple stats list instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_PLAYERMANIALINKPAGEANSWER, $this, 'handleManialinkPageAnswer');
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::ONINIT, $this, 'handleOnInit');
}
/**
* Add the menu entry
*/
public function handleOnInit() {
$this->maniaControl->getCommandManager()->registerCommandListener('stats', $this, 'command_ShowStatsList', false, 'Shows statistics.');
// Action Open StatsList
$this->maniaControl->getManialinkManager()->registerManialinkPageAnswerListener(self::ACTION_OPEN_STATSLIST, $this, 'command_ShowStatsList');
$itemQuad = new Quad_UIConstruction_Buttons();
$itemQuad->setSubStyle($itemQuad::SUBSTYLE_Stats);
$itemQuad->setAction(self::ACTION_OPEN_STATSLIST);
$this->maniaControl->getActionsMenu()->addMenuItem($itemQuad, true, 14, 'Open Statistics');
//TODO settings if a stat get shown
$this->registerStat(PlayerManager::STAT_SERVERTIME, 10, "ST", 20, StatisticManager::STAT_TYPE_TIME);
$this->registerStat(StatisticCollector::STAT_ON_HIT, 20, "H");
$this->registerStat(StatisticCollector::STAT_ON_NEARMISS, 30, "NM");
$this->registerStat(StatisticCollector::STAT_ON_KILL, 40, "K");
$this->registerStat(StatisticCollector::STAT_ON_DEATH, 50, "D");
$this->registerStat(StatisticCollector::STAT_ON_CAPTURE, 60, "C");
$this->registerStat(StatisticManager::SPECIAL_STAT_KD_RATIO, 70, "K/D", 12, StatisticManager::STAT_TYPE_FLOAT);
$this->registerStat(StatisticManager::SPECIAL_STAT_LASER_ACC, 80, "LAcc", 15, StatisticManager::STAT_TYPE_FLOAT);
$this->registerStat(StatisticManager::SPECIAL_STAT_HITS_PH, 85, "H/h", 15, StatisticManager::STAT_TYPE_FLOAT);
}
/**
* Register a Certain Stat
*
* @param string $statName
* @param int $order
* @param string $headShortCut
* @param int $width
* @param string $format
*/
public function registerStat($statName, $order, $headShortCut, $width = 10, $format = StatisticManager::STAT_TYPE_INT) {
// TODO: use own model class
$this->statArray[$order] = array();
$this->statArray[$order]["Name"] = $statName;
$this->statArray[$order]["HeadShortCut"] = '$o' . $headShortCut;
$this->statArray[$order]["Width"] = $width;
$this->statArray[$order]["Format"] = $format;
$this->statsWidth += $width;
}
/**
* Show the stat List
*
* @param array $callback
* @param Player $player
*/
public function command_ShowStatsList(array $callback, Player $player) {
$this->showStatsList($player);
}
/**
* Show the StatsList Widget to the Player
*
* @param Player $player
* @param string $order
*/
public function showStatsList(Player $player, $order = PlayerManager::STAT_SERVERTIME) {
$height = $this->maniaControl->getManialinkManager()->getStyleManager()->getListWidgetsHeight();
$quadStyle = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultMainWindowStyle();
$quadSubstyle = $this->maniaControl->getManialinkManager()->getStyleManager()->getDefaultMainWindowSubStyle();
$maniaLink = new ManiaLink(ManialinkManager::MAIN_MLID);
$width = $this->statsWidth + 60;
//TODO handle size when stats are empty
// Main frame
$frame = new Frame();
$maniaLink->add($frame);
$frame->setSize($width, $height);
$frame->setPosition(0, 0, 10);
// Background
$backgroundQuad = new Quad();
$frame->add($backgroundQuad);
$backgroundQuad->setSize($width, $height);
$backgroundQuad->setStyles($quadStyle, $quadSubstyle);
// Close Quad (X)
$closeQuad = new Quad_Icons64x64_1();
$frame->add($closeQuad);
$closeQuad->setPosition($width * 0.483, $height * 0.467, 3);
$closeQuad->setSize(6, 6);
$closeQuad->setSubStyle(Quad_Icons64x64_1::SUBSTYLE_QuitRace);
$closeQuad->setAction(ManialinkManager::ACTION_CLOSEWIDGET);
// Start offsets
$xStart = -$width / 2;
$posY = $height / 2;
// Predefine Description Label
$descriptionLabel = new Label();
$frame->add($descriptionLabel);
$descriptionLabel->setAlign($descriptionLabel::LEFT, $descriptionLabel::TOP);
$descriptionLabel->setPosition($xStart + 10, -$height / 2 + 5);
$descriptionLabel->setSize($width * 0.7, 4);
$descriptionLabel->setTextSize(2);
$descriptionLabel->setVisible(false);
// Headline
$headFrame = new Frame();
$frame->add($headFrame);
$headFrame->setY($posY - 5);
$posX = $xStart;
$array['$oId'] = $posX + 5;
$array['$oNickname'] = $posX + 14;
// Headline
$posX = $xStart + 55;
$statRankings = array();
foreach ($this->statArray as $key => $stat) {
$ranking = $this->maniaControl->getStatisticManager()->getStatsRanking($stat["Name"]);
if (!empty($ranking)) {
$statRankings[$stat["Name"]] = $ranking;
$array[$stat['HeadShortCut']] = $posX;
$posX += $stat["Width"];
} else {
unset($this->statArray[$key]);
}
}
$labels = $this->maniaControl->getManialinkManager()->labelLine($headFrame, $array);
// Description Label
$index = 2;
foreach ($this->statArray as $statArray) {
if (!isset($labels[$index])) {
break;
}
/** @var Label_Text $label */
$label = $labels[$index];
$label->setAction(self::ACTION_SORT_STATS . '.' . $statArray["Name"]);
$label->addTooltipLabelFeature($descriptionLabel, '$o ' . $statArray["Name"]);
$index++;
}
// define standard properties
$textSize = 1.5;
$textColor = 'fff';
$index = 1;
$posY -= 10;
if (!isset($statRankings[$order])) {
return;
}
foreach ($statRankings[$order] as $playerId => $value) {
$listPlayer = $this->maniaControl->getPlayerManager()->getPlayerByIndex($playerId);
if (!$listPlayer) {
continue;
}
if ($index === 15) {
break;
}
$playerFrame = new Frame();
$frame->add($playerFrame);
// Show current Player Arrow
if ($playerId == $player->index) {
$currentQuad = new Quad_Icons64x64_1();
$playerFrame->add($currentQuad);
$currentQuad->setX($xStart + 3.5);
$currentQuad->setZ(0.2);
$currentQuad->setSize(4, 4);
$currentQuad->setSubStyle($currentQuad::SUBSTYLE_ArrowBlue);
}
$displayArray = array();
foreach ($this->statArray as $stat) {
$statValue = 0;
if (isset($statRankings[$stat['Name']][$playerId])) {
$statValue = $statRankings[$stat['Name']][$playerId];
if ($stat['Format'] == StatisticManager::STAT_TYPE_TIME) {
$statValue = Formatter::formatTimeH($statValue);
} else if ($stat['Format'] == StatisticManager::STAT_TYPE_FLOAT) {
$statValue = round(floatval($statValue), 2);
}
}
$displayArray[$stat['Name']] = array('Value' => strval($statValue), 'Width' => $stat['Width']);
}
$array = array($index => $xStart + 5, $listPlayer->nickname => $xStart + 14);
$this->maniaControl->getManialinkManager()->labelLine($playerFrame, $array);
$posX = $xStart + 55;
foreach ($displayArray as $key => $array) {
$label = new Label_Text();
$playerFrame->add($label);
$label->setHAlign($label::LEFT);
$label->setX($posX);
$label->setStyle($label::STYLE_TextCardSmall);
$label->setTextSize($textSize);
$label->setText($array['Value']);
$label->setTextColor($textColor);
$label->addTooltipLabelFeature($descriptionLabel, '$o ' . $key);
$posX += $array['Width'];
}
$playerFrame->setY($posY);
if ($index % 2 !== 0) {
$lineQuad = new Quad_BgsPlayerCard();
$playerFrame->add($lineQuad);
$lineQuad->setSize($width, 4);
$lineQuad->setSubStyle($lineQuad::SUBSTYLE_BgPlayerCardBig);
$lineQuad->setZ(0.001);
}
$index++;
$posY -= 4;
}
$this->maniaControl->getManialinkManager()->displayWidget($maniaLink, $player, 'SimpleStatsList');
}
/**
* Called on ManialinkPageAnswer
*
* @param array $callback
*/
public function handleManialinkPageAnswer(array $callback) {
$actionId = $callback[1][2];
$actionArray = explode('.', $actionId, 3);
if (count($actionArray) <= 2) {
return;
}
$action = $actionArray[0] . '.' . $actionArray[1];
switch ($action) {
case self::ACTION_SORT_STATS:
$playerLogin = $callback[1][1];
$player = $this->maniaControl->getPlayerManager()->getPlayer($playerLogin);
$this->showStatsList($player, $actionArray[2]);
break;
}
}
}

View File

@ -0,0 +1,371 @@
<?php
namespace ManiaControl\Statistics;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\CallbackManager;
use ManiaControl\Callbacks\Callbacks;
use ManiaControl\ManiaControl;
use ManiaControl\Players\Player;
use ManiaControl\Players\PlayerManager;
/**
* Statistic Collector Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class StatisticCollector implements CallbackListener {
/*
* Constants
*/
const SETTING_COLLECT_STATS_ENABLED = 'Collect Stats Enabled';
const SETTING_COLLECT_STATS_MINPLAYERS = 'Minimum Player Count for Collecting Stats';
const SETTING_ON_SHOOT_PRESTORE = 'Prestore Shots before Insert into Database';
/*
* Statistics
*/
const STAT_PLAYTIME = 'Play Time';
const STAT_MAP_WINS = 'Map Wins';
const STAT_ON_SHOOT = 'Shots';
const STAT_ON_NEARMISS = 'Near Misses';
const STAT_ON_CAPTURE = 'Captures';
const STAT_ON_HIT = 'Hits';
const STAT_ON_GOT_HIT = 'Got Hits';
const STAT_ON_DEATH = 'Deaths';
const STAT_ON_PLAYER_REQUEST_RESPAWN = 'Respawns';
const STAT_ON_KILL = 'Kills';
const STAT_LASER_SHOT = 'Laser Shots';
const STAT_LASER_HIT = 'Laser Hits';
const STAT_ROCKET_SHOT = 'Rocket Shots';
const STAT_ROCKET_HIT = 'Rocket Hits';
const STAT_ARROW_SHOT = 'Arrow Shots';
const STAT_ARROW_HIT = 'Arrow Hits';
const STAT_NUCLEUS_SHOT = 'Nucleus Shots';
const STAT_NUCLEUS_HIT = 'Nucleus Hits';
const SPECIAL_STAT_KILL_DEATH_RATIO = 'Kill / Death';
const WEAPON_LASER = 1;
const WEAPON_ROCKET = 2;
const WEAPON_NUCLEUS = 3;
const WEAPON_ARROW = 5;
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
private $onShootArray = array();
/**
* Construct a new statistic collector instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_MODESCRIPTCALLBACK, $this, 'handleCallbacks');
$this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_MODESCRIPTCALLBACKARRAY, $this, 'handleCallbacks');
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::ONINIT, $this, 'onInit');
$this->maniaControl->getCallbackManager()->registerCallbackListener(PlayerManager::CB_PLAYERDISCONNECT, $this, 'onPlayerDisconnect');
// Settings
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_COLLECT_STATS_ENABLED, true);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_COLLECT_STATS_MINPLAYERS, 4);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_ON_SHOOT_PRESTORE, 10);
}
/**
* Handle ManiaControl OnInit Callback
*/
public function onInit() {
// Define Stats MetaData
$this->maniaControl->getStatisticManager()->defineStatMetaData(self::STAT_PLAYTIME, StatisticManager::STAT_TYPE_TIME);
$this->maniaControl->getStatisticManager()->defineStatMetaData(self::STAT_MAP_WINS);
$this->maniaControl->getStatisticManager()->defineStatMetaData(self::STAT_ON_SHOOT);
$this->maniaControl->getStatisticManager()->defineStatMetaData(self::STAT_ON_NEARMISS);
$this->maniaControl->getStatisticManager()->defineStatMetaData(self::STAT_ON_CAPTURE);
$this->maniaControl->getStatisticManager()->defineStatMetaData(self::STAT_ON_HIT);
$this->maniaControl->getStatisticManager()->defineStatMetaData(self::STAT_ON_GOT_HIT);
$this->maniaControl->getStatisticManager()->defineStatMetaData(self::STAT_ON_DEATH);
$this->maniaControl->getStatisticManager()->defineStatMetaData(self::STAT_ON_PLAYER_REQUEST_RESPAWN);
$this->maniaControl->getStatisticManager()->defineStatMetaData(self::STAT_ON_KILL);
$this->maniaControl->getStatisticManager()->defineStatMetaData(self::STAT_LASER_HIT);
$this->maniaControl->getStatisticManager()->defineStatMetaData(self::STAT_LASER_SHOT);
$this->maniaControl->getStatisticManager()->defineStatMetaData(self::STAT_NUCLEUS_HIT);
$this->maniaControl->getStatisticManager()->defineStatMetaData(self::STAT_NUCLEUS_SHOT);
$this->maniaControl->getStatisticManager()->defineStatMetaData(self::STAT_ROCKET_HIT);
$this->maniaControl->getStatisticManager()->defineStatMetaData(self::STAT_ROCKET_SHOT);
$this->maniaControl->getStatisticManager()->defineStatMetaData(self::STAT_ARROW_HIT);
$this->maniaControl->getStatisticManager()->defineStatMetaData(self::STAT_ARROW_SHOT);
}
/**
* Handle EndMap
*
* @param array $callback
*/
public function onEndMap(array $callback) {
//Check for Minimum PlayerCount
if ($this->maniaControl->getPlayerManager()->getPlayerCount() < $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_COLLECT_STATS_MINPLAYERS)
) {
return;
}
$leaders = $this->maniaControl->getServer()->getRankingManager()->getLeaders();
foreach ($leaders as $leaderLogin) {
$leader = $this->maniaControl->getPlayerManager()->getPlayer($leaderLogin);
$this->maniaControl->getStatisticManager()->incrementStat(self::STAT_MAP_WINS, $leader);
}
}
/**
* Insert OnShoot Statistic when a player leaves
*
* @param Player $player
*/
public function onPlayerDisconnect(Player $player) {
// Check if Stat Collecting is enabled
if (!$this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_COLLECT_STATS_ENABLED)
) {
return;
}
// Insert Data into Database, and destroy player
if (isset($this->onShootArray[$player->login])) {
if ($this->onShootArray[$player->login] > 0) {
$this->maniaControl->getStatisticManager()->insertStat(self::STAT_ON_SHOOT, $player, $this->maniaControl->getServer()->index, $this->onShootArray[$player->login]);
}
unset($this->onShootArray[$player->login]);
}
}
/**
* Handle stats on callbacks
*
* @param array $callback
*/
public function handleCallbacks(array $callback) {
//TODO survivals
// Check if Stat Collecting is enabled
if (!$this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_COLLECT_STATS_ENABLED)
) {
return;
}
// Check for Minimum PlayerCount
if ($this->maniaControl->getPlayerManager()->getPlayerCount() < $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_COLLECT_STATS_MINPLAYERS)
) {
return;
}
$callbackName = $callback[1][0];
switch ($callbackName) {
case 'LibXmlRpc_OnShoot':
$this->handleOnShoot($callback[1][1][0], $callback[1][1][1]);
break;
case 'LibXmlRpc_OnHit':
$shooter = $this->maniaControl->getPlayerManager()->getPlayer($callback[1][1][0]);
$victim = $this->maniaControl->getPlayerManager()->getPlayer($callback[1][1][1]);
$weapon = $callback[1][1][3];
if ($shooter) {
$this->maniaControl->getStatisticManager()->incrementStat($this->getWeaponStat(intval($weapon), false), $shooter);
$this->maniaControl->getStatisticManager()->incrementStat(self::STAT_ON_HIT, $shooter);
}
if ($victim) {
$this->maniaControl->getStatisticManager()->incrementStat(self::STAT_ON_GOT_HIT, $victim);
}
break;
case 'LibXmlRpc_OnNearMiss':
$player = $this->maniaControl->getPlayerManager()->getPlayer($callback[1][1][0]);
$this->maniaControl->getStatisticManager()->incrementStat(self::STAT_ON_NEARMISS, $player);
break;
case 'LibXmlRpc_OnCapture':
$logins = $callback[1][1][0];
$logins = explode(';', $logins);
foreach ($logins as $login) {
$player = $this->maniaControl->getPlayerManager()->getPlayer($login);
if (!$player) {
continue;
}
$this->maniaControl->getStatisticManager()->incrementStat(self::STAT_ON_CAPTURE, $player);
}
break;
case 'LibXmlRpc_OnArmorEmpty':
$victim = $this->maniaControl->getPlayerManager()->getPlayer($callback[1][1][1]);
if (isset($callback[1][1][0])) {
$shooter = $this->maniaControl->getPlayerManager()->getPlayer($callback[1][1][0]);
if ($shooter) {
$this->maniaControl->getStatisticManager()->incrementStat(self::STAT_ON_KILL, $shooter);
}
}
if ($victim) {
$this->maniaControl->getStatisticManager()->incrementStat(self::STAT_ON_DEATH, $victim);
}
break;
case 'LibXmlRpc_OnPlayerRequestRespawn':
$player = $this->maniaControl->getPlayerManager()->getPlayer($callback[1][1][0]);
$this->maniaControl->getStatisticManager()->incrementStat(self::STAT_ON_PLAYER_REQUEST_RESPAWN, $player);
break;
case 'OnShoot':
$paramsObject = json_decode($callback[1][1]);
if ($paramsObject && isset($paramsObject->Event)) {
$this->handleOnShoot($paramsObject->Event->Shooter->Login, $paramsObject->Event->WeaponNum);
}
break;
case 'OnNearMiss':
$paramsObject = json_decode($callback[1][1]);
if ($paramsObject && isset($paramsObject->Event)) {
$player = $this->maniaControl->getPlayerManager()->getPlayer($paramsObject->Event->Shooter->Login);
$this->maniaControl->getStatisticManager()->incrementStat(self::STAT_ON_NEARMISS, $player);
}
break;
case 'OnCapture':
$paramsObject = json_decode($callback[1][1]);
if ($paramsObject && isset($paramsObject->Event)) {
$player = $this->maniaControl->getPlayerManager()->getPlayer($paramsObject->Event->Player->Login);
$this->maniaControl->getStatisticManager()->incrementStat(self::STAT_ON_CAPTURE, $player);
}
break;
case 'OnHit':
$paramsObject = json_decode($callback[1][1]);
if ($paramsObject && isset($paramsObject->Event)) {
$weapon = (int)$paramsObject->Event->WeaponNum;
if (isset($paramsObject->Event->Shooter)) {
$shooter = $this->maniaControl->getPlayerManager()->getPlayer($paramsObject->Event->Shooter->Login);
if ($shooter) {
$this->maniaControl->getStatisticManager()->incrementStat($this->getWeaponStat($weapon, false), $shooter);
$this->maniaControl->getStatisticManager()->incrementStat(self::STAT_ON_HIT, $shooter);
}
}
if (isset($paramsObject->Event->Victim)) {
$victim = $this->maniaControl->getPlayerManager()->getPlayer($paramsObject->Event->Victim->Login);
if ($victim) {
$this->maniaControl->getStatisticManager()->incrementStat(self::STAT_ON_GOT_HIT, $victim);
}
}
}
break;
case 'OnArmorEmpty':
$paramsObject = json_decode($callback[1][1]);
if ($paramsObject && isset($paramsObject->Event)) {
$victim = $this->maniaControl->getPlayerManager()->getPlayer($paramsObject->Event->Victim->Login);
$this->maniaControl->getStatisticManager()->incrementStat(self::STAT_ON_DEATH, $victim);
if (isset($paramsObject->Event->Shooter->Login)) {
$shooter = $this->maniaControl->getPlayerManager()->getPlayer($paramsObject->Event->Shooter->Login);
if ($shooter) {
$this->maniaControl->getStatisticManager()->incrementStat(self::STAT_ON_KILL, $shooter);
}
$this->maniaControl->getStatisticManager()->incrementStat(self::STAT_ON_KILL, $shooter);
}
}
break;
case 'OnRequestRespawn':
$paramsObject = json_decode($callback[1][1]);
if ($paramsObject && isset($paramsObject->Event)) {
$player = $this->maniaControl->getPlayerManager()->getPlayer($paramsObject->Event->Player->Login);
$this->maniaControl->getStatisticManager()->incrementStat(self::STAT_ON_PLAYER_REQUEST_RESPAWN, $player);
}
break;
case 'EndTurn': //TODO make it for other modes working
$paramsObject = json_decode($callback[1][1]);
if ($paramsObject && is_array($paramsObject->ScoresTable)) {
$durationTime = (int)(($paramsObject->EndTime - $paramsObject->StartTime) / 1000);
foreach ($paramsObject->ScoresTable as $score) {
$player = $this->maniaControl->getPlayerManager()->getPlayer($score->Login);
$this->maniaControl->getStatisticManager()->insertStat(self::STAT_PLAYTIME, $player, -1, $durationTime);
}
}
break;
}
}
/**
* Handle Player Shots
*
* @param string $login
* @param int $weaponNumber
*/
private function handleOnShoot($login, $weaponNumber) {
if (!isset($this->onShootArray[$login])) {
$this->onShootArray[$login] = array(self::WEAPON_ROCKET => 0, self::WEAPON_ARROW => 0, self::WEAPON_NUCLEUS => 0, self::WEAPON_LASER => 0);
}
if (!isset($this->onShootArray[$login][$weaponNumber])) {
$this->onShootArray[$login][$weaponNumber] = 0;
}
$this->onShootArray[$login][$weaponNumber]++;
//Write Shoot Data into database
if (array_sum($this->onShootArray[$login]) > $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_ON_SHOOT_PRESTORE)
) {
$player = $this->maniaControl->getPlayerManager()->getPlayer($login);
$rocketShots = $this->onShootArray[$login][self::WEAPON_ROCKET];
$laserShots = $this->onShootArray[$login][self::WEAPON_LASER];
$arrowShots = $this->onShootArray[$login][self::WEAPON_ARROW];
$nucleusShots = $this->onShootArray[$login][self::WEAPON_NUCLEUS];
if ($rocketShots > 0) {
$this->maniaControl->getStatisticManager()->insertStat(self::STAT_ROCKET_SHOT, $player, $this->maniaControl->getServer()->index, $rocketShots);
$this->onShootArray[$login][self::WEAPON_ROCKET] = 0;
}
if ($laserShots > 0) {
$this->maniaControl->getStatisticManager()->insertStat(self::STAT_LASER_SHOT, $player, $this->maniaControl->getServer()->index, $laserShots);
$this->onShootArray[$login][self::WEAPON_LASER] = 0;
}
if ($arrowShots > 0) {
$this->maniaControl->getStatisticManager()->insertStat(self::STAT_ARROW_SHOT, $player, $this->maniaControl->getServer()->index, $arrowShots);
$this->onShootArray[$login][self::WEAPON_ARROW] = 0;
}
if ($nucleusShots > 0) {
$this->maniaControl->getStatisticManager()->insertStat(self::STAT_NUCLEUS_SHOT, $player, $this->maniaControl->getServer()->index, $nucleusShots);
$this->onShootArray[$login][self::WEAPON_NUCLEUS] = 0;
}
$this->maniaControl->getStatisticManager()->insertStat(self::STAT_ON_SHOOT, $player, $this->maniaControl->getServer()->index, $rocketShots + $laserShots + $arrowShots + $nucleusShots);
}
}
/**
* Get the Weapon stat
*
* @param int $weaponNumber
* @param bool $shot
* @return string
*/
private function getWeaponStat($weaponNumber, $shot = true) {
if ($shot) {
switch ($weaponNumber) {
case self::WEAPON_ROCKET:
return self::STAT_ROCKET_SHOT;
case self::WEAPON_LASER:
return self::STAT_LASER_SHOT;
case self::WEAPON_ARROW:
return self::STAT_ARROW_SHOT;
case self::WEAPON_NUCLEUS:
return self::STAT_NUCLEUS_SHOT;
default:
return -1;
}
} else {
switch ($weaponNumber) {
case self::WEAPON_ROCKET:
return self::STAT_ROCKET_HIT;
case self::WEAPON_LASER:
return self::STAT_LASER_HIT;
case self::WEAPON_ARROW:
return self::STAT_ARROW_HIT;
case self::WEAPON_NUCLEUS:
return self::STAT_NUCLEUS_HIT;
default:
return -1;
}
}
}
}

View File

@ -0,0 +1,626 @@
<?php
namespace ManiaControl\Statistics;
use ManiaControl\ManiaControl;
use ManiaControl\Players\Player;
/**
* Statistic Manager Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class StatisticManager {
/*
* Constants
*/
const TABLE_STATMETADATA = 'mc_statmetadata';
const TABLE_STATISTICS = 'mc_statistics';
const STAT_TYPE_INT = '0';
const STAT_TYPE_TIME = '1';
const STAT_TYPE_FLOAT = '2';
const SPECIAL_STAT_KD_RATIO = 'Kill Death Ratio'; //TODO dynamic later
const SPECIAL_STAT_HITS_PH = 'Hits Per Hour';
const SPECIAL_STAT_LASER_ACC = 'Laser Accuracy';
const SPECIAL_STAT_NUCLEUS_ACC = 'Nucleus Accuracy';
const SPECIAL_STAT_ROCKET_ACC = 'Rocket Accuracy';
const SPECIAL_STAT_ARROW_ACC = 'Arrow Accuracy';
/*
* Public properties
*/
/** @var StatisticCollector $statisticCollector */
/** @deprecated see getStatisticCollector() */
public $statisticCollector = null;
/** @var SimpleStatsList $simpleStatsList */
/** @deprecated see getSimpleStatsList() */
public $simpleStatsList = null;
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
private $stats = array();
private $specialStats = array();
/**
* Construct a new statistic manager instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
$this->initTables();
$this->statisticCollector = new StatisticCollector($maniaControl);
$this->simpleStatsList = new SimpleStatsList($maniaControl);
// Store Stats MetaData
$this->storeStatMetaData();
}
/**
* Initialize necessary database tables
*
* @return bool
*/
private function initTables() {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$query = "CREATE TABLE IF NOT EXISTS `" . self::TABLE_STATMETADATA . "` (
`index` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`type` int(5) NOT NULL,
`description` varchar(150) NOT NULL,
PRIMARY KEY (`index`),
UNIQUE KEY `name` (`name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Statistics Meta Data' AUTO_INCREMENT=1;";
$statement = $mysqli->prepare($query);
if ($mysqli->error) {
trigger_error($mysqli->error, E_USER_ERROR);
return false;
}
$statement->execute();
if ($statement->error) {
trigger_error($statement->error, E_USER_ERROR);
return false;
}
$statement->close();
$query = "CREATE TABLE IF NOT EXISTS `" . self::TABLE_STATISTICS . "` (
`index` int(11) NOT NULL AUTO_INCREMENT,
`serverIndex` int(11) NOT NULL,
`playerId` int(11) NOT NULL,
`statId` int(11) NOT NULL,
`value` int(20) NOT NULL DEFAULT '0',
PRIMARY KEY (`index`),
UNIQUE KEY `unique` (`statId`,`playerId`,`serverIndex`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Statistics' AUTO_INCREMENT=1;";
$statement = $mysqli->prepare($query);
if ($mysqli->error) {
trigger_error($mysqli->error, E_USER_ERROR);
return false;
}
$statement->execute();
if ($statement->error) {
trigger_error($statement->error, E_USER_ERROR);
return false;
}
$statement->close();
return true;
}
/**
* Store Stats Meta Data from the Database
*/
private function storeStatMetaData() {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$query = "SELECT * FROM `" . self::TABLE_STATMETADATA . "`;";
$result = $mysqli->query($query);
if (!$result) {
trigger_error($mysqli->error);
return;
}
while ($row = $result->fetch_object()) {
$this->stats[$row->name] = $row;
}
$result->free();
// TODO: own model class
//Define Special Stat Kill / Death Ratio
$stat = new \stdClass();
$stat->name = self::SPECIAL_STAT_KD_RATIO;
$stat->type = self::STAT_TYPE_FLOAT;
$this->specialStats[self::SPECIAL_STAT_KD_RATIO] = $stat;
//Hits Per Hour
$stat = new \stdClass();
$stat->name = self::SPECIAL_STAT_HITS_PH;
$stat->type = self::STAT_TYPE_FLOAT;
$this->specialStats[self::SPECIAL_STAT_HITS_PH] = $stat;
//Laser Accuracy
$stat = new \stdClass();
$stat->name = self::SPECIAL_STAT_LASER_ACC;
$stat->type = self::STAT_TYPE_FLOAT;
$this->specialStats[self::SPECIAL_STAT_LASER_ACC] = $stat;
//Nucleus Accuracy
$stat = new \stdClass();
$stat->name = self::SPECIAL_STAT_NUCLEUS_ACC;
$stat->type = self::STAT_TYPE_FLOAT;
$this->specialStats[self::SPECIAL_STAT_NUCLEUS_ACC] = $stat;
//Arrow Accuracy
$stat = new \stdClass();
$stat->name = self::SPECIAL_STAT_ARROW_ACC;
$stat->type = self::STAT_TYPE_FLOAT;
$this->specialStats[self::SPECIAL_STAT_ARROW_ACC] = $stat;
//Rocket Accuracy
$stat = new \stdClass();
$stat->name = self::SPECIAL_STAT_ROCKET_ACC;
$stat->type = self::STAT_TYPE_FLOAT;
$this->specialStats[self::SPECIAL_STAT_ROCKET_ACC] = $stat;
}
/**
* Return the statistic collector
*
* @return StatisticCollector
*/
public function getStatisticCollector() {
return $this->statisticCollector;
}
/**
* Return the simple stats list
*
* @return SimpleStatsList
*/
public function getSimpleStatsList() {
return $this->simpleStatsList;
}
/**
* Get All statistics ordered by an given name
*
* @param string $statName
* @param $serverIndex
* @param $minValue
* @internal param $orderedBy
* @return array
*/
public function getStatsRanking($statName = '', $serverIndex = -1, $minValue = -1) {
if (isset($this->specialStats[$statName])) {
return $this->getStatsRankingOfSpecialStat($statName, $serverIndex);
}
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$statId = $this->getStatId($statName);
$query = "SELECT `playerId`, `serverIndex`, `value` FROM `" . self::TABLE_STATISTICS . "`
WHERE `statId` = {$statId} ";
if ($minValue >= 0) {
$query .= "AND `value` >= {$minValue} ";
}
$query .= "ORDER BY `value` DESC;";
$result = $mysqli->query($query);
if (!$result) {
trigger_error($mysqli->error);
return null;
}
$stats = array();
while ($row = $result->fetch_object()) {
if ($serverIndex < 0) {
if (!isset($stats[$row->playerId])) {
$stats[$row->playerId] = $row->value;
} else {
$stats[$row->playerId] += $row->value;
}
} else if ($serverIndex == $row->serverIndex) {
$stats[$row->playerId] = $row->value;
}
}
$result->free();
arsort($stats);
return $stats;
}
/**
* Gets The Ranking of an Special Stat
*
* @param string $statName
* @param $serverIndex
* @return array
*/
public function getStatsRankingOfSpecialStat($statName = '', $serverIndex = -1) {
$statsArray = array();
switch ($statName) {
case self::SPECIAL_STAT_KD_RATIO:
$kills = $this->getStatsRanking(StatisticCollector::STAT_ON_KILL, $serverIndex);
$deaths = $this->getStatsRanking(StatisticCollector::STAT_ON_DEATH, $serverIndex);
if (!$kills || !$deaths) {
return array();
}
foreach ($deaths as $key => $death) {
if (!$death || !isset($kills[$key])) {
continue;
}
$statsArray[$key] = intval($kills[$key]) / intval($death);
}
arsort($statsArray);
break;
case self::SPECIAL_STAT_HITS_PH:
$hits = $this->getStatsRanking(StatisticCollector::STAT_ON_HIT, $serverIndex);
$times = $this->getStatsRanking(StatisticCollector::STAT_PLAYTIME, $serverIndex);
if (!$hits || !$times) {
return array();
}
foreach ($times as $key => $time) {
if (!$time || !isset($hits[$key])) {
continue;
}
$statsArray[$key] = intval($hits[$key]) / (intval($time) / 3600);
}
arsort($statsArray);
break;
case self::SPECIAL_STAT_ARROW_ACC:
$hits = $this->getStatsRanking(StatisticCollector::STAT_ARROW_HIT, $serverIndex);
$shots = $this->getStatsRanking(StatisticCollector::STAT_ARROW_SHOT, $serverIndex);
if (!$hits || !$shots) {
return array();
}
foreach ($shots as $key => $shot) {
if (!$shot || !isset($hits[$key])) {
continue;
}
$statsArray[$key] = intval($hits[$key]) / (intval($shot));
}
arsort($statsArray);
break;
case self::SPECIAL_STAT_LASER_ACC:
$hits = $this->getStatsRanking(StatisticCollector::STAT_LASER_HIT, $serverIndex);
$shots = $this->getStatsRanking(StatisticCollector::STAT_LASER_SHOT, $serverIndex);
if (!$hits || !$shots) {
return array();
}
foreach ($shots as $key => $shot) {
if (!$shot || !isset($hits[$key])) {
continue;
}
$statsArray[$key] = intval($hits[$key]) / (intval($shot));
}
arsort($statsArray);
break;
case self::SPECIAL_STAT_ROCKET_ACC:
$hits = $this->getStatsRanking(StatisticCollector::STAT_ROCKET_HIT, $serverIndex);
$shots = $this->getStatsRanking(StatisticCollector::STAT_ROCKET_SHOT, $serverIndex);
if (!$hits || !$shots) {
return array();
}
foreach ($shots as $key => $shot) {
if (!$shot || !isset($hits[$key])) {
continue;
}
$statsArray[$key] = intval($hits[$key]) / (intval($shot));
}
arsort($statsArray);
break;
case self::SPECIAL_STAT_NUCLEUS_ACC:
$hits = $this->getStatsRanking(StatisticCollector::STAT_NUCLEUS_HIT, $serverIndex);
$shots = $this->getStatsRanking(StatisticCollector::STAT_NUCLEUS_SHOT, $serverIndex);
if (!$hits || !$shots) {
return array();
}
foreach ($shots as $key => $shot) {
if (!$shot || !isset($hits[$key])) {
continue;
}
$statsArray[$key] = intval($hits[$key]) / (intval($shot));
}
arsort($statsArray);
break;
}
return $statsArray;
}
/**
* Return the Stat Id
*
* @param string $statName
* @return int
*/
private function getStatId($statName) {
if (isset($this->stats[$statName])) {
$stat = $this->stats[$statName];
return (int)$stat->index;
}
return null;
}
/**
* Get all statistics of a certain player
*
* @param Player $player
* @param int $serverIndex
* @return array
*/
public function getAllPlayerStats(Player $player, $serverIndex = -1) {
// TODO improve performance of the foreach
$playerStats = array();
foreach ($this->stats as $stat) {
$value = $this->getStatisticData($stat->name, $player->index, $serverIndex);
$playerStats[$stat->name] = array($stat, $value);
}
foreach ($this->specialStats as $stat) {
switch ($stat->name) {
case self::SPECIAL_STAT_KD_RATIO:
if (!isset($playerStats[StatisticCollector::STAT_ON_KILL]) || !isset($playerStats[StatisticCollector::STAT_ON_DEATH])) {
continue;
}
$kills = intval($playerStats[StatisticCollector::STAT_ON_KILL][1]);
$deaths = intval($playerStats[StatisticCollector::STAT_ON_DEATH][1]);
if (!$deaths) {
continue;
}
$playerStats[$stat->name] = array($stat, $kills / $deaths);
break;
case self::SPECIAL_STAT_HITS_PH:
if (!isset($playerStats[StatisticCollector::STAT_PLAYTIME]) || !isset($playerStats[StatisticCollector::STAT_ON_HIT])) {
continue;
}
$hits = intval($playerStats[StatisticCollector::STAT_ON_HIT][1]);
$time = intval($playerStats[StatisticCollector::STAT_PLAYTIME][1]);
if (!$time) {
continue;
}
$playerStats[$stat->name] = array($stat, $hits / ($time / 3600));
break;
case self::SPECIAL_STAT_ARROW_ACC:
if (!isset($playerStats[StatisticCollector::STAT_ARROW_HIT]) || !isset($playerStats[StatisticCollector::STAT_ARROW_SHOT])) {
continue;
}
$hits = intval($playerStats[StatisticCollector::STAT_ARROW_HIT][1]);
$shots = intval($playerStats[StatisticCollector::STAT_ARROW_SHOT][1]);
if (!$shots) {
continue;
}
$playerStats[$stat->name] = array($stat, $hits / $shots);
break;
case self::SPECIAL_STAT_LASER_ACC:
if (!isset($playerStats[StatisticCollector::STAT_LASER_HIT]) || !isset($playerStats[StatisticCollector::STAT_LASER_SHOT])) {
continue;
}
$hits = intval($playerStats[StatisticCollector::STAT_LASER_HIT][1]);
$shots = intval($playerStats[StatisticCollector::STAT_LASER_SHOT][1]);
if (!$shots) {
continue;
}
$playerStats[$stat->name] = array($stat, $hits / $shots);
break;
case self::SPECIAL_STAT_ROCKET_ACC:
if (!isset($playerStats[StatisticCollector::STAT_ROCKET_HIT]) || !isset($playerStats[StatisticCollector::STAT_ROCKET_SHOT])) {
continue;
}
$hits = intval($playerStats[StatisticCollector::STAT_ROCKET_HIT][1]);
$shots = intval($playerStats[StatisticCollector::STAT_ROCKET_SHOT][1]);
if (!$shots) {
continue;
}
$playerStats[$stat->name] = array($stat, $hits / $shots);
break;
case self::SPECIAL_STAT_NUCLEUS_ACC:
if (!isset($playerStats[StatisticCollector::STAT_NUCLEUS_HIT]) || !isset($playerStats[StatisticCollector::STAT_NUCLEUS_SHOT])) {
continue;
}
$hits = intval($playerStats[StatisticCollector::STAT_NUCLEUS_HIT][1]);
$shots = intval($playerStats[StatisticCollector::STAT_NUCLEUS_SHOT][1]);
if (!$shots) {
continue;
}
$playerStats[$stat->name] = array($stat, (float)($hits / $shots));
break;
}
}
return $playerStats;
}
/**
* Get the value of an statistic
*
* @param $statName
* @param $playerId
* @param int $serverIndex
* @return int
*/
public function getStatisticData($statName, $playerId, $serverIndex = -1) {
//Handle Special Stats
switch ($statName) {
case self::SPECIAL_STAT_KD_RATIO:
$kills = $this->getStatisticData(StatisticCollector::STAT_ON_KILL, $playerId, $serverIndex);
$deaths = $this->getStatisticData(StatisticCollector::STAT_ON_DEATH, $playerId, $serverIndex);
if (!$deaths) {
return -1;
}
return intval($kills) / intval($deaths);
case self::SPECIAL_STAT_HITS_PH:
$hits = $this->getStatisticData(StatisticCollector::STAT_ON_HIT, $playerId, $serverIndex);
$time = $this->getStatisticData(StatisticCollector::STAT_PLAYTIME, $playerId, $serverIndex);
if (!$time) {
return -1;
}
return intval($hits) / (intval($time) / 3600);
case self::SPECIAL_STAT_ARROW_ACC:
$hits = $this->getStatisticData(StatisticCollector::STAT_ARROW_HIT, $playerId, $serverIndex);
$shots = $this->getStatisticData(StatisticCollector::STAT_ARROW_SHOT, $playerId, $serverIndex);
if (!$shots) {
return -1;
}
return intval($hits) / intval($shots);
case self::SPECIAL_STAT_LASER_ACC:
$hits = $this->getStatisticData(StatisticCollector::STAT_LASER_HIT, $playerId, $serverIndex);
$shots = $this->getStatisticData(StatisticCollector::STAT_LASER_SHOT, $playerId, $serverIndex);
if (!$shots) {
return -1;
}
return intval($hits) / intval($shots);
case self::SPECIAL_STAT_NUCLEUS_ACC:
$hits = $this->getStatisticData(StatisticCollector::STAT_NUCLEUS_HIT, $playerId, $serverIndex);
$shots = $this->getStatisticData(StatisticCollector::STAT_NUCLEUS_SHOT, $playerId, $serverIndex);
if (!$shots) {
return -1;
}
return intval($hits) / intval($shots);
case self::SPECIAL_STAT_ROCKET_ACC:
$hits = $this->getStatisticData(StatisticCollector::STAT_ROCKET_HIT, $playerId, $serverIndex);
$shots = $this->getStatisticData(StatisticCollector::STAT_ROCKET_SHOT, $playerId, $serverIndex);
if (!$shots) {
return -1;
}
return intval($hits) / intval($shots);
}
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$statId = $this->getStatId($statName);
if (!$statId) {
return -1;
}
if ($serverIndex < 0) {
$query = "SELECT SUM(`value`) as `value` FROM `" . self::TABLE_STATISTICS . "`
WHERE `statId` = {$statId}
AND `playerId` = {$playerId};";
} else {
$query = "SELECT `value` FROM `" . self::TABLE_STATISTICS . "`
WHERE `statId` = {$statId}
AND `playerId` = {$playerId}
AND `serverIndex` = {$serverIndex};";
}
$result = $mysqli->query($query);
if (!$result) {
trigger_error($mysqli->error);
return -1;
}
$row = $result->fetch_object();
$result->free();
return $row->value;
}
/**
* Increments a Statistic by one
*
* @param string $statName
* @param Player $player
* @param int $serverIndex
* @return bool
*/
public function incrementStat($statName, Player $player, $serverIndex = -1) {
return $this->insertStat($statName, $player, $serverIndex, 1);
}
/**
* Inserts a Stat into the database
*
* @param string $statName
* @param Player $player
* @param int $serverIndex
* @param mixed $value , value to Add
* @param string $statType
* @return bool
*/
public function insertStat($statName, Player $player, $serverIndex = -1, $value, $statType = self::STAT_TYPE_INT) {
// TODO: statType isn't used
if (!$player) {
return false;
}
if ($player->isFakePlayer()) {
return true;
}
$statId = $this->getStatId($statName);
if (!$statId) {
return false;
}
if ($value < 1) {
return false;
}
if ($serverIndex) {
$serverIndex = $this->maniaControl->getServer()->index;
}
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$query = "INSERT INTO `" . self::TABLE_STATISTICS . "` (
`serverIndex`,
`playerId`,
`statId`,
`value`
) VALUES (
?, ?, ?, ?
) ON DUPLICATE KEY UPDATE
`value` = `value` + VALUES(`value`);";
$statement = $mysqli->prepare($query);
if ($mysqli->error) {
trigger_error($mysqli->error);
return false;
}
$statement->bind_param('iiii', $serverIndex, $player->index, $statId, $value);
$statement->execute();
if ($statement->error) {
trigger_error($statement->error);
$statement->close();
return false;
}
$statement->close();
return true;
}
/**
* Defines a Stat
*
* @param $statName
* @param string $type
* @param string $statDescription
* @return bool
*/
public function defineStatMetaData($statName, $type = self::STAT_TYPE_INT, $statDescription = '') {
$mysqli = $this->maniaControl->getDatabase()->getMysqli();
$query = "INSERT INTO `" . self::TABLE_STATMETADATA . "` (
`name`,
`type`,
`description`
) VALUES (
?, ?, ?
) ON DUPLICATE KEY UPDATE
`type` = VALUES(`type`),
`description` = VALUES(`description`);";
$statement = $mysqli->prepare($query);
if ($mysqli->error) {
trigger_error($mysqli->error);
return false;
}
$statement->bind_param('sis', $statName, $type, $statDescription);
$statement->execute();
if ($statement->error) {
trigger_error($statement->error);
$statement->close();
return false;
}
$statement->close();
return true;
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace ManiaControl\Update;
/**
* Plugin Update Data Model Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class PluginUpdateData {
/*
* Public properties
*/
public $pluginId = null;
public $pluginName = null;
public $pluginAuthor = null;
public $pluginDescription = null;
public $id = null;
public $version = null;
public $zipfile = null;
public $url = null;
/**
* Construct new plugin update data instance
*
* @param object $updateData
*/
public function __construct($updateData) {
$this->pluginId = $updateData->id;
$this->pluginName = $updateData->name;
$this->pluginAuthor = $updateData->author;
$this->pluginDescription = $updateData->description;
if ($updateData->currentVersion) {
$this->id = $updateData->currentVersion->id;
$this->version = $updateData->currentVersion->version;
$this->zipfile = $updateData->currentVersion->zipfile;
$this->url = $updateData->currentVersion->url;
}
}
/**
* Check if the plugin update data is newer than the given plugin version
*
* @param float $version
* @return bool
*/
public function isNewerThan($version) {
if (!$version) {
return true;
}
return ($this->version > $version);
}
}

View File

@ -0,0 +1,402 @@
<?php
namespace ManiaControl\Update;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\CallbackManager;
use ManiaControl\Callbacks\TimerListener;
use ManiaControl\Commands\CommandListener;
use ManiaControl\Files\BackupUtil;
use ManiaControl\Files\FileUtil;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
use ManiaControl\Players\Player;
use ManiaControl\Plugins\InstallMenu;
use ManiaControl\Plugins\Plugin;
use ManiaControl\Plugins\PluginManager;
use ManiaControl\Plugins\PluginMenu;
use ManiaControl\Utils\WebReader;
/**
* Manager checking for ManiaControl Plugin Updates
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class PluginUpdateManager implements CallbackListener, CommandListener, TimerListener {
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/**
* Create a new plugin update manager instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_PLAYERMANIALINKPAGEANSWER, $this, 'handleManialinkPageAnswer');
// Chat commands
$this->maniaControl->getCommandManager()->registerCommandListener('checkpluginsupdate', $this, 'handle_CheckPluginsUpdate', true, 'Check for Plugin Updates.');
$this->maniaControl->getCommandManager()->registerCommandListener('pluginsupdate', $this, 'handle_PluginsUpdate', true, 'Perform the Plugin Updates.');
}
/**
* Handle //checkpluginsupdate command
*
* @param array $chatCallback
* @param Player $player
*/
public function handle_CheckPluginsUpdate(array $chatCallback, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, UpdateManager::SETTING_PERMISSION_UPDATECHECK)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$this->checkPluginsUpdate($player);
}
/**
* Check if there are Outdated Plugins installed
*
* @param Player $player
*/
public function checkPluginsUpdate(Player $player = null) {
$message = 'Checking Plugins for newer Versions...';
if ($player) {
$this->maniaControl->getChat()->sendInformation($message, $player);
}
Logger::log($message);
$this->maniaControl->getPluginManager()->fetchPluginList(function ($data, $error) use (&$player) {
if (!$data || $error) {
$message = 'Error while checking Plugins for newer Versions!';
if ($player) {
$this->maniaControl->getChat()->sendError($message, $player);
}
Logger::logError($message);
return;
}
$pluginsData = $this->parsePluginsData($data);
$pluginClasses = $this->maniaControl->getPluginManager()->getPluginClasses();
$pluginUpdates = array();
foreach ($pluginClasses as $pluginClass) {
/** @var Plugin $pluginClass */
$pluginId = $pluginClass::getId();
if (!isset($pluginsData[$pluginId])) {
continue;
}
/** @var PluginUpdateData $pluginData */
$pluginData = $pluginsData[$pluginId];
$pluginVersion = $pluginClass::getVersion();
if ($pluginData->isNewerThan($pluginVersion)) {
$pluginUpdates[$pluginId] = $pluginData;
$message = "There is an Update of '{$pluginData->pluginName}' available! ('{$pluginClass}' - Version {$pluginData->version})";
if ($player) {
$this->maniaControl->getChat()->sendSuccess($message, $player);
}
Logger::log($message);
}
}
if (empty($pluginUpdates)) {
$message = 'Plugins Update Check completed: All Plugins are up-to-date!';
if ($player) {
$this->maniaControl->getChat()->sendSuccess($message, $player);
}
Logger::log($message);
} else {
$updatesCount = count($pluginUpdates);
$message = "Plugins Update Check completed: There are {$updatesCount} Updates available!";
if ($player) {
$this->maniaControl->getChat()->sendSuccess($message, $player);
}
Logger::log($message);
}
});
}
/**
* Get an Array of Plugin Update Data from the given Web Service Result
*
* @param mixed $webServiceResult
* @return mixed
*/
public function parsePluginsData($webServiceResult) {
if (!$webServiceResult || !is_array($webServiceResult)) {
return false;
}
$pluginsData = array();
foreach ($webServiceResult as $pluginResult) {
$pluginData = new PluginUpdateData($pluginResult);
$pluginsData[$pluginData->pluginId] = $pluginData;
}
return $pluginsData;
}
/**
* Handle //pluginsupdate command
*
* @param array $chatCallback
* @param Player $player
*/
public function handle_PluginsUpdate(array $chatCallback, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, UpdateManager::SETTING_PERMISSION_UPDATE)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$this->performPluginsUpdate($player);
}
/**
* Perform an Update of all outdated Plugins
*
* @param Player $player
*/
public function performPluginsUpdate(Player $player = null) {
$pluginsUpdates = $this->getPluginsUpdates();
if (empty($pluginsUpdates)) {
$message = 'There are no Plugin Updates available!';
if ($player) {
$this->maniaControl->getChat()->sendInformation($message, $player);
}
Logger::log($message);
return;
}
$message = "Starting Plugins Updating...";
if ($player) {
$this->maniaControl->getChat()->sendInformation($message, $player);
}
Logger::log($message);
$performBackup = $this->maniaControl->getSettingManager()->getSettingValue($this->maniaControl->getUpdateManager(), UpdateManager::SETTING_PERFORM_BACKUPS);
if ($performBackup && !BackupUtil::performPluginsBackup()) {
$message = 'Creating Backup before Plugins Update failed!';
if ($player) {
$this->maniaControl->getChat()->sendError($message, $player);
}
Logger::logError($message);
}
foreach ($pluginsUpdates as $pluginUpdateData) {
$this->installPlugin($pluginUpdateData, $player, true);
}
}
/**
* Check for Plugin Updates
*
* @return mixed
*/
public function getPluginsUpdates() {
$url = ManiaControl::URL_WEBSERVICE . 'plugins';
$response = WebReader::getUrl($url);
$dataJson = $response->getContent();
$pluginData = json_decode($dataJson);
if (!$pluginData || empty($pluginData)) {
return false;
}
$pluginsUpdates = $this->parsePluginsData($pluginData);
$updates = array();
$pluginClasses = $this->maniaControl->getPluginManager()->getPluginClasses();
foreach ($pluginClasses as $pluginClass) {
/** @var Plugin $pluginClass */
$pluginId = $pluginClass::getId();
if (isset($pluginsUpdates[$pluginId])) {
/** @var PluginUpdateData $pluginUpdateData */
$pluginUpdateData = $pluginsUpdates[$pluginId];
$pluginVersion = $pluginClass::getVersion();
if ($pluginUpdateData->isNewerThan($pluginVersion)) {
$updates[$pluginId] = $pluginUpdateData;
}
}
}
if (empty($updates)) {
return false;
}
return $updates;
}
/**
* Load the given Plugin Update Data
*
* @param PluginUpdateData $pluginUpdateData
* @param Player $player
* @param bool $update
*/
private function installPlugin(PluginUpdateData $pluginUpdateData, Player $player = null, $update = false) {
$this->maniaControl->getFileReader()->loadFile($pluginUpdateData->url, function ($updateFileContent, $error) use (
&$pluginUpdateData, &$player, &$update
) {
if (!$updateFileContent || $error) {
$message = "Error loading Update Data for '{$pluginUpdateData->pluginName}': {$error}!";
if ($player) {
$this->maniaControl->getChat()->sendInformation($message, $player);
}
Logger::logError($message);
return;
}
$actionNoun = ($update ? 'Update' : 'Install');
$actionVerb = ($update ? 'Updating' : 'Installing');
$actionVerbDone = ($update ? 'updated' : 'installed');
$message = "Now {$actionVerb} '{$pluginUpdateData->pluginName}'...";
if ($player) {
$this->maniaControl->getChat()->sendInformation($message, $player);
}
Logger::log($message);
$tempDir = FileUtil::getTempFolder();
$updateFileName = $tempDir . $pluginUpdateData->zipfile;
$bytes = file_put_contents($updateFileName, $updateFileContent);
if (!$bytes || $bytes <= 0) {
$message = "Plugin {$actionNoun} failed: Couldn't save {$actionNoun} Zip!";
if ($player) {
$this->maniaControl->getChat()->sendError($message, $player);
}
Logger::logError($message);
return;
}
$zip = new \ZipArchive();
$result = $zip->open($updateFileName);
if ($result !== true) {
$message = "Plugin {$actionNoun} failed: Couldn't open {$actionNoun} Zip! ({$result})";
if ($player) {
$this->maniaControl->getChat()->sendError($message, $player);
}
Logger::logError($message);
return;
}
$zip->extractTo(MANIACONTROL_PATH . 'plugins' . DIRECTORY_SEPARATOR);
$zip->close();
unlink($updateFileName);
FileUtil::deleteTempFolder();
$messageExtra = '';
if ($update) {
$messageExtra = ' (Restart ManiaControl to load the new Version!)';
}
$message = "Successfully {$actionVerbDone} '{$pluginUpdateData->pluginName}'!{$messageExtra}";
if ($player) {
$this->maniaControl->getChat()->sendSuccess($message, $player);
}
Logger::log($message);
if (!$update) {
$newPluginClasses = $this->maniaControl->getPluginManager()->loadPlugins();
if (empty($newPluginClasses)) {
$message = "Loading fresh installed Plugin '{$pluginUpdateData->pluginName}' failed!";
if ($player) {
$this->maniaControl->getChat()->sendError($message, $player);
}
Logger::log($message);
} else {
$message = "Successfully loaded fresh installed Plugin '{$pluginUpdateData->pluginName}'!";
if ($player) {
$this->maniaControl->getChat()->sendSuccess($message, $player);
}
Logger::log($message);
$this->maniaControl->getConfigurator()->showMenu($player, InstallMenu::getTitle());
}
}
});
}
/**
* Handle PlayerManialinkPageAnswer callback
*
* @param array $callback
*/
public function handleManialinkPageAnswer(array $callback) {
$actionId = $callback[1][2];
$update = (strpos($actionId, PluginMenu::ACTION_PREFIX_UPDATEPLUGIN) === 0);
$install = (strpos($actionId, InstallMenu::ACTION_PREFIX_INSTALL_PLUGIN) === 0);
if (!$update && !$install) {
return;
}
$login = $callback[1][1];
$player = $this->maniaControl->getPlayerManager()->getPlayer($login);
if ($update) {
$pluginClass = substr($actionId, strlen(PluginMenu::ACTION_PREFIX_UPDATEPLUGIN));
if ($pluginClass === 'All') {
$this->performPluginsUpdate($player);
} else {
$pluginUpdateData = $this->getPluginUpdate($pluginClass);
if ($pluginUpdateData) {
$this->installPlugin($pluginUpdateData, $player, true);
} else {
$message = 'Error loading Plugin Update Data!';
$this->maniaControl->getChat()->sendError($message, $player);
}
}
} else {
$pluginId = substr($actionId, strlen(InstallMenu::ACTION_PREFIX_INSTALL_PLUGIN));
$url = ManiaControl::URL_WEBSERVICE . 'plugins/' . $pluginId;
$this->maniaControl->getFileReader()->loadFile($url, function ($data, $error) use (&$player) {
if ($error || !$data) {
$message = "Error loading Plugin Install Data! {$error}";
$this->maniaControl->getChat()->sendError($message, $player);
return;
}
$data = json_decode($data);
if (!$data) {
$message = "Error loading Plugin Install Data! {$error}";
$this->maniaControl->getChat()->sendError($message, $player);
return;
}
$pluginUpdateData = new PluginUpdateData($data);
$this->installPlugin($pluginUpdateData, $player);
});
}
}
/**
* Check given Plugin Class for Update
*
* @param string $pluginClass
* @return mixed
*/
public function getPluginUpdate($pluginClass) {
$pluginClass = PluginManager::getPluginClass($pluginClass);
/** @var Plugin $pluginClass */
$pluginId = $pluginClass::getId();
$url = ManiaControl::URL_WEBSERVICE . 'plugins/' . $pluginId;
$response = WebReader::getUrl($url);
$dataJson = $response->getContent();
$pluginVersion = json_decode($dataJson);
if (!$pluginVersion) {
return false;
}
$pluginUpdateData = new PluginUpdateData($pluginVersion);
$version = $pluginClass::getVersion();
if ($pluginUpdateData->isNewerThan($version)) {
return $pluginUpdateData;
}
return false;
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace ManiaControl\Update;
/**
* ManiaControl Update Data Model Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class UpdateData {
/*
* Public properties
*/
public $version = null;
public $channel = null;
public $url = null;
public $releaseDate = null;
public $minDedicatedBuild = null;
/**
* Construct new update data instance
*
* @param object $updateData
*/
public function __construct($updateData) {
$this->version = $updateData->version;
$this->channel = $updateData->channel;
$this->url = $updateData->url;
$this->releaseDate = $updateData->release_date;
$this->minDedicatedBuild = $updateData->min_dedicated_build;
}
/**
* Check if the update data is newer than the given date
*
* @param string $compareDate
* @return bool
*/
public function isNewerThan($compareDate) {
if (!$compareDate) {
return true;
}
$compareTime = strtotime($compareDate);
$releaseTime = strtotime($this->releaseDate);
return ($releaseTime > $compareTime);
}
}

View File

@ -0,0 +1,515 @@
<?php
namespace ManiaControl\Update;
use ManiaControl\Admin\AuthenticationManager;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\TimerListener;
use ManiaControl\Commands\CommandListener;
use ManiaControl\Files\BackupUtil;
use ManiaControl\Files\FileUtil;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
use ManiaControl\Players\Player;
use ManiaControl\Players\PlayerManager;
/**
* Manager checking for ManiaControl Core Updates
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class UpdateManager implements CallbackListener, CommandListener, TimerListener {
/*
* Constants
*/
const CHANNEL_RELEASE = 'release';
const CHANNEL_BETA = 'beta';
const CHANNEL_NIGHTLY = 'nightly';
const SETTING_ENABLE_UPDATECHECK = 'Enable Automatic Core Update Check';
const SETTING_UPDATECHECK_INTERVAL = 'Core Update Check Interval (Hours)';
const SETTING_UPDATECHECK_CHANNEL = 'Core Update Channel (release, beta, nightly)';
const SETTING_PERFORM_BACKUPS = 'Perform Backup before Updating';
const SETTING_AUTO_UPDATE = 'Perform update automatically';
const SETTING_PERMISSION_UPDATE = 'Update Core';
const SETTING_PERMISSION_UPDATECHECK = 'Check Core Update';
const BUILD_DATE_FILE_NAME = 'build_date.txt';
/*
* Public properties
*/
/** @var PluginUpdateManager $pluginUpdateManager
* @deprecated see getPluginUpdateManager()
*/
public $pluginUpdateManager = null;
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
private $currentBuildDate = null;
/** @var UpdateData $coreUpdateData */
private $coreUpdateData = null;
/**
* Construct a new update manager instance
*
* @param ManiaControl $maniaControl
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
// Settings
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_ENABLE_UPDATECHECK, true);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_AUTO_UPDATE, true);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_UPDATECHECK_INTERVAL, 1);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_UPDATECHECK_CHANNEL, $this->getUpdateChannels());
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_PERFORM_BACKUPS, true);
// Callbacks
$updateInterval = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_UPDATECHECK_INTERVAL);
$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');
// Permissions
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_UPDATE, AuthenticationManager::AUTH_LEVEL_ADMIN);
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_UPDATECHECK, AuthenticationManager::AUTH_LEVEL_MODERATOR);
// Chat commands
$this->maniaControl->getCommandManager()->registerCommandListener('checkupdate', $this, 'handle_CheckUpdate', true, 'Checks if there is a core update.');
$this->maniaControl->getCommandManager()->registerCommandListener('coreupdate', $this, 'handle_CoreUpdate', true, 'Performs the core update.');
// Children
$this->pluginUpdateManager = new PluginUpdateManager($maniaControl);
}
/**
* Get the possible update channels
*
* @return string[]
*/
public function getUpdateChannels() {
// TODO: change default channel on release
return array(self::CHANNEL_BETA, self::CHANNEL_RELEASE, self::CHANNEL_NIGHTLY);
}
/**
* Return the plugin update manager
*
* @return PluginUpdateManager
*/
public function getPluginUpdateManager() {
return $this->pluginUpdateManager;
}
/**
* Perform Hourly Update Check
*/
public function hourlyUpdateCheck() {
$updateCheckEnabled = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_ENABLE_UPDATECHECK);
if (!$updateCheckEnabled) {
$this->setCoreUpdateData();
} else {
$this->checkUpdate();
}
}
/**
* Set Core Update Data
*
* @param UpdateData $coreUpdateData
*/
public function setCoreUpdateData(UpdateData $coreUpdateData = null) {
$this->coreUpdateData = $coreUpdateData;
}
/**
* Start an Update Check
*/
public function checkUpdate() {
$this->checkCoreUpdateAsync(array($this, 'handleUpdateCheck'));
}
/**
* Checks a Core Update asynchronously
*
* @param callable $function
*/
public function checkCoreUpdateAsync($function) {
$updateChannel = $this->getCurrentUpdateChannelSetting();
$url = ManiaControl::URL_WEBSERVICE . 'versions?current=1&channel=' . $updateChannel;
$this->maniaControl->getFileReader()->loadFile($url, function ($dataJson, $error) use (&$function) {
if ($error) {
Logger::logError('Error on UpdateCheck: ' . $error);
return;
}
$versions = json_decode($dataJson);
if (!$versions || !isset($versions[0])) {
call_user_func($function);
} else {
$updateData = new UpdateData($versions[0]);
call_user_func($function, $updateData);
}
});
}
/**
* Retrieve the Update Channel Setting
*
* @return string
*/
public function getCurrentUpdateChannelSetting() {
$updateChannel = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_UPDATECHECK_CHANNEL);
$updateChannel = strtolower($updateChannel);
if (!in_array($updateChannel, $this->getUpdateChannels())) {
$updateChannel = self::CHANNEL_RELEASE;
}
return $updateChannel;
}
/**
* Handle the fetched Update Data of the hourly Check
*
* @param UpdateData $updateData
*/
public function handleUpdateCheck(UpdateData $updateData = null) {
if (!$this->checkUpdateData($updateData)) {
// No new update available
return;
}
if (!$this->checkUpdateDataBuildVersion($updateData)) {
// Server incompatible
Logger::logError("Please update Your Server to '{$updateData->minDedicatedBuild}' in order to receive further Updates!");
return;
}
if ($this->coreUpdateData != $updateData) {
if ($this->isNightlyUpdateChannel()) {
Logger::log("New Nightly Build ({$updateData->releaseDate}) available!");
} else {
Logger::log("New ManiaControl Version {$updateData->version} available!");
}
$this->setCoreUpdateData($updateData);
}
$this->checkAutoUpdate();
}
/**
* Check if the given Update Data has a new Version and fits for the Server
*
* @param UpdateData $updateData
* @return bool
*/
public function checkUpdateData(UpdateData $updateData = null) {
if (!$updateData || !$updateData->url) {
// Data corrupted
return false;
}
$isNightly = $this->isNightlyUpdateChannel();
$buildDate = $this->getBuildDate();
if ($isNightly || $buildDate) {
return $updateData->isNewerThan($buildDate);
}
return ($updateData->version > ManiaControl::VERSION);
}
/**
* Check if ManiaControl is running the Nightly Update Channel
*
* @param string $updateChannel
* @return bool
*/
public function isNightlyUpdateChannel($updateChannel = null) {
if (!$updateChannel) {
$updateChannel = $this->getCurrentUpdateChannelSetting();
}
return ($updateChannel === self::CHANNEL_NIGHTLY);
}
/**
* Get the build date of the local version
*
* @return string
*/
public function getBuildDate() {
if (!$this->currentBuildDate) {
$nightlyBuildDateFile = MANIACONTROL_PATH . 'core' . DIRECTORY_SEPARATOR . self::BUILD_DATE_FILE_NAME;
if (file_exists($nightlyBuildDateFile)) {
$this->currentBuildDate = file_get_contents($nightlyBuildDateFile);
}
}
return $this->currentBuildDate;
}
/**
* Check if the Update Data is compatible with the Server
*
* @param UpdateData $updateData
* @return bool
*/
public function checkUpdateDataBuildVersion(UpdateData $updateData = null) {
if (!$updateData) {
// Data corrupted
return false;
}
$version = $this->maniaControl->getClient()->getVersion();
if ($updateData->minDedicatedBuild > $version->build) {
// Server not compatible
return false;
}
return true;
}
/**
* Check if an automatic Update should be performed
*/
public function checkAutoUpdate() {
$autoUpdate = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_AUTO_UPDATE);
if (!$autoUpdate) {
// Auto update turned off
return;
}
if (!$this->coreUpdateData) {
// No update available
return;
}
if ($this->maniaControl->getPlayerManager()->getPlayerCount(false) > 0
) {
// Server not empty
return;
}
$this->performCoreUpdate();
}
/**
* Perform a Core Update
*
* @param Player $player
* @return bool
*/
public function performCoreUpdate(Player $player = null) {
if (!$this->coreUpdateData) {
$message = 'Update failed: No update Data available!';
if ($player) {
$this->maniaControl->getChat()->sendError($message, $player);
}
Logger::logError($message);
return false;
}
Logger::log("Starting Update to Version v{$this->coreUpdateData->version}...");
$directories = array('core', 'plugins');
if (!FileUtil::checkWritePermissions($directories)) {
$message = 'Update not possible: Incorrect File System Permissions!';
if ($player) {
$this->maniaControl->getChat()->sendError($message, $player);
}
Logger::logError($message);
return false;
}
$performBackup = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_PERFORM_BACKUPS);
if ($performBackup && !BackupUtil::performFullBackup()) {
$message = 'Creating Backup before Update failed!';
if ($player) {
$this->maniaControl->getChat()->sendError($message, $player);
}
Logger::logError($message);
}
$updateData = $this->coreUpdateData;
$this->maniaControl->getFileReader()->loadFile($updateData->url, function ($updateFileContent, $error) use (
$updateData, &$player
) {
if (!$updateFileContent || $error) {
$message = "Update failed: Couldn't load Update zip! {$error}";
if ($player) {
$this->maniaControl->getChat()->sendError($message, $player);
}
Logger::logError($message);
return;
}
$tempDir = FileUtil::getTempFolder();
if (!$tempDir) {
$message = "Update failed: Can't save Update zip!";
if ($player) {
$this->maniaControl->getChat()->sendError($message, $player);
}
Logger::logError($message);
return;
}
$updateFileName = $tempDir . basename($updateData->url);
$bytes = file_put_contents($updateFileName, $updateFileContent);
if (!$bytes || $bytes <= 0) {
$message = "Update failed: Couldn't save Update zip!";
if ($player) {
$this->maniaControl->getChat()->sendError($message, $player);
}
Logger::logError($message);
return;
}
$zip = new \ZipArchive();
$result = $zip->open($updateFileName);
if ($result !== true) {
$message = "Update failed: Couldn't open Update Zip. ({$result})";
if ($player) {
$this->maniaControl->getChat()->sendError($message, $player);
}
Logger::logError($message);
unlink($updateFileName);
return;
}
$zip->extractTo(MANIACONTROL_PATH);
$zip->close();
unlink($updateFileName);
FileUtil::deleteTempFolder();
// Set the build date
$this->setBuildDate($updateData->releaseDate);
$message = 'Update finished!';
if ($player) {
$this->maniaControl->getChat()->sendSuccess($message, $player);
}
Logger::log($message);
$this->maniaControl->restart();
});
return true;
}
/**
* Set the build date version
*
* @param string $date
* @return bool
*/
public function setBuildDate($date) {
$nightlyBuildDateFile = MANIACONTROL_PATH . 'core' . DIRECTORY_SEPARATOR . self::BUILD_DATE_FILE_NAME;
$success = (bool)file_put_contents($nightlyBuildDateFile, $date);
$this->currentBuildDate = $date;
return $success;
}
/**
* Handle ManiaControl PlayerJoined callback
*
* @param Player $player
*/
public function handlePlayerJoined(Player $player) {
if (!$this->coreUpdateData) {
return;
}
// Announce available update
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_UPDATE)
) {
return;
}
if ($this->isNightlyUpdateChannel()) {
$this->maniaControl->getChat()->sendSuccess('New Nightly Build (' . $this->coreUpdateData->releaseDate . ') available!', $player->login);
} else {
$this->maniaControl->getChat()->sendInformation('New ManiaControl Version ' . $this->coreUpdateData->version . ' available!', $player->login);
}
}
/**
* Handle Player Disconnect Callback
*
* @param Player $player
*/
public function handlePlayerDisconnect(Player $player) {
$this->checkAutoUpdate();
}
/**
* Handle //checkupdate command
*
* @param array $chatCallback
* @param Player $player
*/
public function handle_CheckUpdate(array $chatCallback, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_UPDATECHECK)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$this->checkCoreUpdateAsync(function (UpdateData $updateData = null) use (&$player) {
if (!$this->checkUpdateData($updateData)) {
$this->maniaControl->getChat()->sendInformation('No Update available!', $player->login);
return;
}
if (!$this->checkUpdateDataBuildVersion($updateData)) {
$this->maniaControl->getChat()->sendError("Please update Your Server to '{$updateData->minDedicatedBuild}' in order to receive further Updates!", $player->login);
return;
}
$isNightly = $this->isNightlyUpdateChannel();
if ($isNightly) {
$buildDate = $this->getBuildDate();
if ($buildDate) {
if ($updateData->isNewerThan($buildDate)) {
$this->maniaControl->getChat()->sendInformation("No new Build available! (Current Build: '{$buildDate}')", $player->login);
return;
} else {
$this->maniaControl->getChat()->sendSuccess("New Nightly Build ({$updateData->releaseDate}) available! (Current Build: '{$buildDate}')", $player->login);
}
} else {
$this->maniaControl->getChat()->sendSuccess("New Nightly Build ('{$updateData->releaseDate}') available!", $player->login);
}
} else {
$this->maniaControl->getChat()->sendSuccess('Update for Version ' . $updateData->version . ' available!', $player->login);
}
$this->coreUpdateData = $updateData;
});
}
/**
* Handle //coreupdate command
*
* @param array $chatCallback
* @param Player $player
*/
public function handle_CoreUpdate(array $chatCallback, Player $player) {
if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_UPDATE)
) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
$this->checkCoreUpdateAsync(function (UpdateData $updateData = null) use (&$player) {
if (!$updateData) {
$this->maniaControl->getChat()->sendError('Update is currently not possible!', $player);
return;
}
if (!$this->checkUpdateDataBuildVersion($updateData)) {
$this->maniaControl->getChat()->sendError("The Next ManiaControl Update requires a newer Dedicated Server Version!", $player);
return;
}
$this->coreUpdateData = $updateData;
$this->performCoreUpdate($player);
});
}
}

30
core/Utils/ClassUtil.php Normal file
View File

@ -0,0 +1,30 @@
<?php
namespace ManiaControl\Utils;
/**
* Utility Class offering Methods related to Classes
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
abstract class ClassUtil {
/**
* Get the Class Name of the given Object
*
* @param mixed $object
* @return string
*/
public static function getClass($object) {
if (is_object($object)) {
return get_class($object);
}
if (is_string($object)) {
return $object;
}
trigger_error("Invalid class param: '" . print_r($object, true) . "'!");
return (string)$object;
}
}

57
core/Utils/ColorUtil.php Normal file
View File

@ -0,0 +1,57 @@
<?php
namespace ManiaControl\Utils;
/**
* Utility Class offering Methods to convert and use ManiaPlanet Colors
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
abstract class ColorUtil {
/**
* Convert the given float value to a color code from red to green
*
* @param float $value
* @return string
*/
public static function floatToStatusColor($value) {
$value = floatval($value);
$red = 1.;
$green = 1.;
if ($value < 0.5) {
$green = $value * 2.;
}
if ($value > 0.5) {
$red = 2. * (1. - $value);
}
$red = ColorUtil::floatToCode($red);
$green = ColorUtil::floatToCode($green);
return $red . $green . '0';
}
/**
* Get hex color representation of the float
*
* @param float $value
* @return string
*/
public static function floatToCode($value) {
$value = floatval($value);
if ($value < 0.) {
$value = 0.;
}
if ($value > 1.) {
$value = 1.;
}
$value *= 15.;
$value = (int)round($value);
if ($value < 10) {
return (string)$value;
}
$codes = array(10 => 'a', 11 => 'b', 12 => 'c', 13 => 'd', 14 => 'e', 15 => 'f');
return $codes[$value];
}
}

View File

@ -0,0 +1,45 @@
<?php
namespace ManiaControl\Utils;
/**
* Command Line Helper Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class CommandLineHelper {
/**
* Get the command line parameter value with the given name
*
* @param string $paramName
* @return string
*/
public static function getParameter($paramName) {
$paramName = (string)$paramName;
$params = self::getAllParameters();
foreach ($params as $param) {
$parts = explode('=', $param, 2);
if (count($parts) < 2) {
continue;
}
if ($parts[0] !== $paramName) {
continue;
}
return $parts[1];
}
return null;
}
/**
* Get all command line parameters
*
* @return array
*/
public static function getAllParameters() {
global $argv;
return (array)$argv;
}
}

196
core/Utils/Formatter.php Normal file
View File

@ -0,0 +1,196 @@
<?php
namespace ManiaControl\Utils;
use ManiaControl\Logger;
/**
* Class offering Methods to format Texts and Values
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
abstract class Formatter {
/**
* Return the given Text with Escaping around it
*
* @param string $text
* @return string
*/
public static function escapeText($text) {
return '$<' . $text . '$>';
}
/**
* Format the given Time (in Milliseconds)
*
* @param int $time
* @return string
*/
public static function formatTime($time) {
// TODO: use gmdate()
$time = (int)$time;
$milliseconds = $time % 1000;
$seconds = floor($time / 1000);
$minutes = floor($seconds / 60);
$hours = floor($minutes / 60);
$minutes -= $hours * 60;
$seconds -= $hours * 60 + $minutes * 60;
$format = ($hours > 0 ? $hours . ':' : '');
$format .= ($hours > 0 && $minutes < 10 ? '0' : '') . $minutes . ':';
$format .= ($seconds < 10 ? '0' : '') . $seconds . ':';
$format .= ($milliseconds < 100 ? '0' : '') . ($milliseconds < 10 ? '0' : '') . $milliseconds;
return $format;
}
/**
* Format an elapsed time String (2 days ago...) by a given timestamp
*
* @param int $ptime
* @return string
*/
public static function time_elapsed_string($ptime) {
// TODO: refactor code: camelCase!
$etime = time() - $ptime;
if ($etime < 1) {
return '0 seconds';
}
$a = array(12 * 30 * 24 * 60 * 60 => 'year', 30 * 24 * 60 * 60 => 'month', 24 * 60 * 60 => 'day', 60 * 60 => 'hour', 60 => 'minute', 1 => 'second');
foreach ($a as $secs => $str) {
$d = $etime / $secs;
if ($d >= 1) {
$r = round($d);
return $r . ' ' . $str . ($r > 1 ? 's' : '') . ' ago';
}
}
return '';
}
/**
* Format the given Time (Seconds) to hh:mm:ss
*
* @param int $seconds
* @return string
*/
public static function formatTimeH($seconds) {
return gmdate('H:i:s', $seconds);
}
/**
* Convert the given Time (Seconds) to MySQL Timestamp
*
* @param int $seconds
* @return string
*/
public static function formatTimestamp($seconds) {
return date('Y-m-d H:i:s', $seconds);
}
/**
* Remove possibly dangerous Codes
* (Dangerous Codes are Links and Formats that might screw up the following Styling)
*
* @param string $string
* @return string
*/
public static function stripDirtyCodes($string) {
$string = self::stripLinks($string);
$string = preg_replace('/(?<!\$)((?:\$\$)*)\$[ow<>]/iu', '$1', $string);
return $string;
}
/**
* Remove Links from the String
*
* @param string $string
* @return string
*/
public static function stripLinks($string) {
return preg_replace('/(?<!\$)((?:\$\$)*)\$[hlp](?:\[.*?\])?(.*?)(?:\$[hlp]|(\$z)|$)/iu', '$1$2$3', $string);
}
/**
* Remove all Codes from the String
*
* @param string $string
* @return string
*/
public static function stripCodes($string) {
$string = self::stripLinks($string);
$string = self::stripColors($string);
$string = preg_replace('/(?<!\$)((?:\$\$)*)\$[^$0-9a-hlp]/iu', '$1', $string);
return $string;
}
/**
* Remove Colors from the String
*
* @param string $string
* @return string
*/
public static function stripColors($string) {
return preg_replace('/(?<!\$)((?:\$\$)*)\$(?:g|[0-9a-f][^\$]{0,2})/iu', '$1', $string);
}
/**
* Map Country Names to 3-letter Nation Abbreviations
* Created by Xymph
* Based on http://en.wikipedia.org/wiki/List_of_IOC_country_codes
* See also http://en.wikipedia.org/wiki/Comparison_of_IOC,_FIFA,_and_ISO_3166_country_codes
*
* @param string $country
* @return string
*/
public static function mapCountry($country) {
$nations = array('Afghanistan' => 'AFG', 'Albania' => 'ALB', 'Algeria' => 'ALG', 'Andorra' => 'AND', 'Angola' => 'ANG', 'Argentina' => 'ARG', 'Armenia' => 'ARM', 'Aruba' => 'ARU', 'Australia' => 'AUS', 'Austria' => 'AUT', 'Azerbaijan' => 'AZE', 'Bahamas' => 'BAH', 'Bahrain' => 'BRN', 'Bangladesh' => 'BAN', 'Barbados' => 'BAR', 'Belarus' => 'BLR', 'Belgium' => 'BEL', 'Belize' => 'BIZ', 'Benin' => 'BEN', 'Bermuda' => 'BER', 'Bhutan' => 'BHU', 'Bolivia' => 'BOL', 'Bosnia&Herzegovina' => 'BIH', 'Botswana' => 'BOT', 'Brazil' => 'BRA', 'Brunei' => 'BRU', 'Bulgaria' => 'BUL', 'Burkina Faso' => 'BUR', 'Burundi' => 'BDI', 'Cambodia' => 'CAM', 'Cameroon' => 'CAR', // actually CMR
'Canada' => 'CAN', 'Cape Verde' => 'CPV', 'Central African Republic' => 'CAF', 'Chad' => 'CHA', 'Chile' => 'CHI', 'China' => 'CHN', 'Chinese Taipei' => 'TPE', 'Colombia' => 'COL', 'Congo' => 'CGO', 'Costa Rica' => 'CRC', 'Croatia' => 'CRO', 'Cuba' => 'CUB', 'Cyprus' => 'CYP', 'Czech Republic' => 'CZE', 'Czech republic' => 'CZE', 'DR Congo' => 'COD', 'Denmark' => 'DEN', 'Djibouti' => 'DJI', 'Dominica' => 'DMA', 'Dominican Republic' => 'DOM', 'Ecuador' => 'ECU', 'Egypt' => 'EGY', 'El Salvador' => 'ESA', 'Eritrea' => 'ERI', 'Estonia' => 'EST', 'Ethiopia' => 'ETH', 'Fiji' => 'FIJ', 'Finland' => 'FIN', 'France' => 'FRA', 'Gabon' => 'GAB', 'Gambia' => 'GAM', 'Georgia' => 'GEO', 'Germany' => 'GER', 'Ghana' => 'GHA', 'Greece' => 'GRE', 'Grenada' => 'GRN', 'Guam' => 'GUM', 'Guatemala' => 'GUA', 'Guinea' => 'GUI', 'Guinea-Bissau' => 'GBS', 'Guyana' => 'GUY', 'Haiti' => 'HAI', 'Honduras' => 'HON', 'Hong Kong' => 'HKG', 'Hungary' => 'HUN', 'Iceland' => 'ISL', 'India' => 'IND', 'Indonesia' => 'INA', 'Iran' => 'IRI', 'Iraq' => 'IRQ', 'Ireland' => 'IRL', 'Israel' => 'ISR', 'Italy' => 'ITA', 'Ivory Coast' => 'CIV', 'Jamaica' => 'JAM', 'Japan' => 'JPN', 'Jordan' => 'JOR', 'Kazakhstan' => 'KAZ', 'Kenya' => 'KEN', 'Kiribati' => 'KIR', 'Korea' => 'KOR', 'Kuwait' => 'KUW', 'Kyrgyzstan' => 'KGZ', 'Laos' => 'LAO', 'Latvia' => 'LAT', 'Lebanon' => 'LIB', 'Lesotho' => 'LES', 'Liberia' => 'LBR', 'Libya' => 'LBA', 'Liechtenstein' => 'LIE', 'Lithuania' => 'LTU', 'Luxembourg' => 'LUX', 'Macedonia' => 'MKD', 'Malawi' => 'MAW', 'Malaysia' => 'MAS', 'Mali' => 'MLI', 'Malta' => 'MLT', 'Mauritania' => 'MTN', 'Mauritius' => 'MRI', 'Mexico' => 'MEX', 'Moldova' => 'MDA', 'Monaco' => 'MON', 'Mongolia' => 'MGL', 'Montenegro' => 'MNE', 'Morocco' => 'MAR', 'Mozambique' => 'MOZ', 'Myanmar' => 'MYA', 'Namibia' => 'NAM', 'Nauru' => 'NRU', 'Nepal' => 'NEP', 'Netherlands' => 'NED', 'New Zealand' => 'NZL', 'Nicaragua' => 'NCA', 'Niger' => 'NIG', 'Nigeria' => 'NGR', 'Norway' => 'NOR', 'Oman' => 'OMA', 'Other Countries' => 'OTH', 'Pakistan' => 'PAK', 'Palau' => 'PLW', 'Palestine' => 'PLE', 'Panama' => 'PAN', 'Paraguay' => 'PAR', 'Peru' => 'PER', 'Philippines' => 'PHI', 'Poland' => 'POL', 'Portugal' => 'POR', 'Puerto Rico' => 'PUR', 'Qatar' => 'QAT', 'Romania' => 'ROM', // actually ROU
'Russia' => 'RUS', 'Rwanda' => 'RWA', 'Samoa' => 'SAM', 'San Marino' => 'SMR', 'Saudi Arabia' => 'KSA', 'Senegal' => 'SEN', 'Serbia' => 'SCG', // actually SRB
'Sierra Leone' => 'SLE', 'Singapore' => 'SIN', 'Slovakia' => 'SVK', 'Slovenia' => 'SLO', 'Somalia' => 'SOM', 'South Africa' => 'RSA', 'Spain' => 'ESP', 'Sri Lanka' => 'SRI', 'Sudan' => 'SUD', 'Suriname' => 'SUR', 'Swaziland' => 'SWZ', 'Sweden' => 'SWE', 'Switzerland' => 'SUI', 'Syria' => 'SYR', 'Taiwan' => 'TWN', 'Tajikistan' => 'TJK', 'Tanzania' => 'TAN', 'Thailand' => 'THA', 'Togo' => 'TOG', 'Tonga' => 'TGA', 'Trinidad and Tobago' => 'TRI', 'Tunisia' => 'TUN', 'Turkey' => 'TUR', 'Turkmenistan' => 'TKM', 'Tuvalu' => 'TUV', 'Uganda' => 'UGA', 'Ukraine' => 'UKR', 'United Arab Emirates' => 'UAE', 'United Kingdom' => 'GBR', 'United States of America' => 'USA', 'Uruguay' => 'URU', 'Uzbekistan' => 'UZB', 'Vanuatu' => 'VAN', 'Venezuela' => 'VEN', 'Vietnam' => 'VIE', 'Yemen' => 'YEM', 'Zambia' => 'ZAM', 'Zimbabwe' => 'ZIM');
if (array_key_exists($country, $nations)) {
return $nations[$country];
}
if ($country) {
Logger::logWarning("Couldn't map Country: '{$country}'!");
}
return 'OTH';
}
/**
* Parse the given Value into a Bool
*
* @param mixed $value
* @return bool
*/
public static function parseBoolean($value) {
if (is_string($value)) {
$value = strtolower($value);
}
return filter_var($value, FILTER_VALIDATE_BOOLEAN);
}
/**
* Make sure the given Text is encoded in UTF-8
*
* @param string $text
* @return string
*/
public static function utf8($text) {
if (is_array($text)) {
$newArray = array();
foreach ($text as $key => $value) {
if (is_string($value)) {
$newArray[$key] = self::utf8($value);
} else {
$newArray[$key] = $value;
}
}
return $newArray;
}
return mb_convert_encoding($text, 'UTF-8', 'UTF-8');
}
}

188
core/Utils/SystemUtil.php Normal file
View File

@ -0,0 +1,188 @@
<?php
namespace ManiaControl\Utils;
use ManiaControl\Logger;
/**
* System Utility Class
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
class SystemUtil {
/*
* Constants
*/
const OS_UNIX = 'Unix';
const OS_WIN = 'Windows';
const MIN_PHP_VERSION = '5.4';
/**
* Get whether ManiaControl is running on Windows
*
* @return bool
*/
public static function isWindows() {
return (self::getOS() === self::OS_WIN);
}
/**
* Get the Operating System on which ManiaControl is running
*
* @return string
*/
public static function getOS() {
if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
return self::OS_WIN;
}
return self::OS_UNIX;
}
/**
* Check for the requirements to run ManiaControl
*/
public static function checkRequirements() {
$success = true;
// Check for min PHP version
$phpVersion = phpversion();
$message = 'Checking for minimum required PHP-Version ' . self::MIN_PHP_VERSION . ' ... ';
if ($phpVersion < self::MIN_PHP_VERSION) {
Logger::log($message . $phpVersion . ' TOO OLD VERSION!');
Logger::log(' -- Make sure that you install at least PHP ' . self::MIN_PHP_VERSION . '!');
$success = false;
} else {
Logger::log($message . self::MIN_PHP_VERSION . ' OK!');
}
// Check for MySQLi
$message = 'Checking for installed MySQLi ... ';
if (!extension_loaded('mysqli')) {
Logger::log($message . 'NOT FOUND!');
Logger::log(" -- You don't have MySQLi installed! Check: http://www.php.net/manual/en/mysqli.installation.php");
$success = false;
} else {
Logger::log($message . 'FOUND!');
}
// Check for cURL
$message = 'Checking for installed cURL ... ';
if (!extension_loaded('curl')) {
Logger::log($message . 'NOT FOUND!');
Logger::log(" -- You don't have cURL installed! Check: http://www.php.net/manual/en/curl.installation.php");
$success = false;
} else {
Logger::log($message . 'FOUND!');
}
if (!$success) {
// Missing requirements
self::quit();
}
}
/**
* Stop ManiaControl immediately
*
* @param string $message
* @param bool $errorPrefix
*/
public static function quit($message = null, $errorPrefix = false) {
if ($message) {
if ($errorPrefix) {
Logger::logError($message);
} else {
Logger::log($message);
}
}
exit;
}
/**
* Restart ManiaControl immediately
*/
public static function restart() {
if (SystemUtil::isUnix()) {
self::restartUnix();
} else {
self::restartWindows();
}
}
/**
* Get whether ManiaControl is running on Unix
*
* @return bool
*/
public static function isUnix() {
return (self::getOS() === self::OS_UNIX);
}
/**
* Perform restart on Unix
*/
private static function restartUnix() {
if (!SystemUtil::checkFunctionAvailability('exec')) {
Logger::log("Can't restart ManiaControl because the function 'exec' is disabled!");
return;
}
$fileName = null;
if ($scriptName = CommandLineHelper::getParameter('-sh')) {
$fileName = $scriptName;
} else {
$fileName = 'ManiaControl.sh';
}
$filePath = MANIACONTROL_PATH . $fileName;
if (!is_readable($filePath)) {
Logger::log("Can't restart ManiaControl because the file '{$fileName}' doesn't exist or isn't readable!");
return;
}
$command = 'sh ' . escapeshellarg($filePath) . ' > /dev/null &';
exec($command);
}
/**
* Check whether the given function is available
*
* @param string $functionName
* @return bool
*/
public static function checkFunctionAvailability($functionName) {
return (function_exists($functionName) && !in_array($functionName, self::getDisabledFunctions()));
}
/**
* Get the array of disabled functions
*
* @return array
*/
protected static function getDisabledFunctions() {
$disabledText = ini_get('disable_functions');
return explode(',', $disabledText);
}
/**
* Perform restart on Windows
*/
private static function restartWindows() {
if (!SystemUtil::checkFunctionAvailability('system')) {
Logger::log("Can't restart ManiaControl because the function 'system' is disabled!");
return;
}
$fileName = null;
if ($scriptName = CommandLineHelper::getParameter('-bat')) {
$fileName = $scriptName;
} else {
$fileName = 'ManiaControl.bat';
}
$filePath = MANIACONTROL_PATH . $fileName;
if (!is_readable($filePath)) {
Logger::log("Can't restart ManiaControl because the file '{$fileName}' doesn't exist or isn't readable!");
return;
}
$command = escapeshellarg($filePath);
system($command); // TODO: windows stops here as long as controller is running
}
}

96
core/Utils/WebReader.php Normal file
View File

@ -0,0 +1,96 @@
<?php
namespace ManiaControl\Utils;
use cURL\Request;
use cURL\Response;
use ManiaControl\ManiaControl;
/**
* Reader Utility Class for efficient Web Requests
*
* @author ManiaControl Team <mail@maniacontrol.com>
* @copyright 2014 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
abstract class WebReader {
/**
* Load a URL via GET
*
* @param string $url
* @param callable $function
* @return Response
*/
public static function getUrl($url, callable $function = null) {
$request = static::newRequest($url);
$response = $request->send();
if ($function) {
static::performCallback($response, $function);
}
return $response;
}
/**
* @deprecated
* @see WebReader::getUrl()
*/
public static function loadUrl($url, callable $function = null) {
if ($function) {
return static::getUrl($url, $function);
}
return static::getUrl($url);
}
/**
* Create a new cURL Request for the given URL
*
* @param string $url
* @return Request
*/
protected static function newRequest($url) {
$request = new Request($url);
$options = $request->getOptions();
$options->set(CURLOPT_TIMEOUT, 5) // timeout
->set(CURLOPT_HEADER, false) // don't display response header
->set(CURLOPT_CRLF, true) // linux line feed
->set(CURLOPT_ENCODING, '') // accept encoding
->set(CURLOPT_USERAGENT, 'ManiaControl v' . ManiaControl::VERSION) // user-agent
->set(CURLOPT_RETURNTRANSFER, true) // return instead of output content
->set(CURLOPT_AUTOREFERER, true); // follow redirects
return $request;
}
/**
* Perform the given callback function with the response
*
* @param Response $response
* @param callable $function
*/
protected static function performCallback(Response $response, callable $function) {
$content = $response->getContent();
$error = $response->getError()->getMessage();
call_user_func($function, $content, $error);
}
/**
* Load a URL via POST
*
* @param string $url
* @param string $content
* @param callable $function
* @return Response
*/
public static function postUrl($url, $content = null, callable $function = null) {
$request = static::newRequest($url);
$request->getOptions()->set(CURLOPT_POST, true); // post method
if ($content) {
$request->getOptions()->set(CURLOPT_POSTFIELDS, $content); // post content field
}
$response = $request->send();
if ($function) {
static::performCallback($response, $function);
}
return $response;
}
}