2013-11-09 17:24:03 +01:00
< ? php
2013-11-19 20:29:37 +01:00
namespace ManiaControl\Server ;
2014-01-03 20:57:24 +01:00
use ManiaControl\Callbacks\CallbackListener ;
2014-05-24 16:39:12 +02:00
use ManiaControl\Callbacks\Callbacks ;
2015-07-19 16:48:34 +02:00
use ManiaControl\Commands\CommandListener ;
2017-03-26 19:44:55 +02:00
use ManiaControl\General\UsageInformationAble ;
use ManiaControl\General\UsageInformationTrait ;
2014-08-05 01:49:13 +02:00
use ManiaControl\Logger ;
2013-11-19 20:29:37 +01:00
use ManiaControl\ManiaControl ;
2014-05-13 16:40:05 +02:00
use ManiaControl\Players\Player ;
2017-03-26 13:11:30 +02:00
use ManiaControl\Script\ScriptManager ;
2014-05-13 16:40:05 +02:00
use ManiaControl\Utils\CommandLineHelper ;
2014-02-13 14:21:25 +01:00
use Maniaplanet\DedicatedServer\Xmlrpc\Exception ;
2013-11-19 20:29:37 +01:00
2013-11-09 17:24:03 +01:00
/**
2014-07-25 16:28:47 +02:00
* Class providing access to the connected ManiaPlanet Server
2014-05-02 17:50:30 +02:00
*
* @ author ManiaControl Team < mail @ maniacontrol . com >
2019-01-05 21:02:24 +01:00
* @ copyright 2014 - 2019 ManiaControl Team
2014-05-02 17:50:30 +02:00
* @ license http :// www . gnu . org / licenses / GNU General Public License , Version 3
2013-11-09 17:24:03 +01:00
*/
2017-03-26 19:44:55 +02:00
class Server implements CallbackListener , CommandListener , UsageInformationAble {
use UsageInformationTrait ;
2017-04-13 19:26:57 +02:00
2014-03-20 16:18:35 +01:00
/*
2014-01-03 20:57:24 +01:00
* Constants
*/
2014-05-11 16:02:29 +02:00
const TABLE_SERVERS = 'mc_servers' ;
const CB_TEAM_MODE_CHANGED = 'Server.TeamModeChanged' ;
2014-05-02 17:50:30 +02:00
2014-03-20 16:18:35 +01:00
/*
2014-07-25 16:28:47 +02:00
* Public properties
2014-01-06 15:54:39 +01:00
*/
2014-06-20 19:05:59 +02:00
/** @var Config $config */
2014-10-24 20:08:21 +02:00
public $config = null ;
public $index = - 1 ;
public $ip = null ;
public $port = - 1 ;
2014-01-06 15:54:39 +01:00
public $p2pPort = - 1 ;
2014-10-24 20:08:21 +02:00
public $login = null ;
2014-01-06 15:54:39 +01:00
public $titleId = null ;
2017-04-13 19:26:57 +02:00
/** @var Directory $directory */
private $directory = null ;
/** @var Commands $commands */
private $commands = null ;
/** @var UsageReporter $usageReporter */
private $usageReporter = null ;
/** @var RankingManager $rankingManager */
private $rankingManager = null ;
/** @var \ManiaControl\Script\ScriptManager $scriptManager */
private $scriptManager = null ;
2014-05-02 17:50:30 +02:00
2014-03-20 16:18:35 +01:00
/*
2014-08-02 22:31:46 +02:00
* Private properties
2013-11-09 17:24:03 +01:00
*/
2014-08-02 22:31:46 +02:00
/** @var ManiaControl $maniaControl */
2013-11-10 20:09:08 +01:00
private $maniaControl = null ;
2014-10-24 20:08:21 +02:00
private $teamMode = null ;
2013-11-09 17:24:03 +01:00
/**
2014-01-06 15:54:39 +01:00
* Construct a new Server
2014-05-02 17:50:30 +02:00
*
2014-01-03 20:57:24 +01:00
* @ param ManiaControl $maniaControl
2013-11-09 17:24:03 +01:00
*/
2013-11-10 20:09:08 +01:00
public function __construct ( ManiaControl $maniaControl ) {
$this -> maniaControl = $maniaControl ;
2014-01-05 14:41:19 +01:00
$this -> initTables ();
2014-05-02 17:50:30 +02:00
2014-06-20 15:58:41 +02:00
$this -> directory = new Directory ( $maniaControl );
2014-07-19 23:29:20 +02:00
$this -> commands = new Commands ( $maniaControl );
2014-05-11 16:02:29 +02:00
$this -> usageReporter = new UsageReporter ( $maniaControl );
$this -> rankingManager = new RankingManager ( $maniaControl );
$this -> scriptManager = new ScriptManager ( $maniaControl );
2014-05-02 17:50:30 +02:00
2014-08-03 01:34:18 +02:00
// Callbacks
2014-08-13 11:05:52 +02:00
$this -> maniaControl -> getCallbackManager () -> registerCallbackListener ( Callbacks :: ONINIT , $this , 'onInit' );
2015-07-19 16:48:34 +02:00
$this -> maniaControl -> getCommandManager () -> registerCommandListener ( " uptime " , $this , " chatUpTime " , true , " Show how long the server is running. " );
}
/**
* Displays how long the Server is running already in the Chat
*
* @ param array $chatCallback
* @ param \ManiaControl\Players\Player $player
*/
public function chatUpTime ( array $chatCallback , Player $player ) {
$networkStats = $this -> maniaControl -> getClient () -> getNetworkStats ();
$minutestotal = $networkStats -> uptime / 60 ;
$hourstotal = $minutestotal / 60 ;
$days = intval ( $hourstotal / 24 );
$hours = intval ( $hourstotal - 24 * $days );
$minutes = intval ( $minutestotal - 24 * 60 * $days - $hours * 60 );
$days > 1 ? $dayString = 'days' : $dayString = 'day' ;
$hours > 1 ? $hourString = 'hours' : $hourString = 'hour' ;
$minutes > 1 ? $minuteString = 'minutes' : $minuteString = 'minute' ;
$this -> maniaControl -> getChat () -> sendChat ( 'Server is running since $<$fff' . $days . '$> ' . $dayString . ', $<$fff' . $hours . '$> ' . $hourString . ' and $<$fff' . $minutes . '$> ' . $minuteString , $player );
2014-01-06 15:54:39 +01:00
}
2014-05-02 17:50:30 +02:00
/**
* Initialize necessary Database Tables
*
* @ return bool
*/
private function initTables () {
2014-08-13 11:05:52 +02:00
$mysqli = $this -> maniaControl -> getDatabase () -> getMysqli ();
2014-05-02 17:50:30 +02:00
$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 ;
}
2014-07-25 16:28:47 +02:00
/**
* Return the server config
*
* @ return Config
*/
public function getConfig () {
return $this -> config ;
}
/**
* Return the server commands
*
* @ return Commands
*/
public function getCommands () {
return $this -> commands ;
}
/**
* Return the usage reporter
*
* @ return UsageReporter
*/
public function getUsageReporter () {
return $this -> usageReporter ;
}
2014-08-02 22:31:46 +02:00
/**
* Return the ranking manager
*
* @ return RankingManager
*/
public function getRankingManager () {
return $this -> rankingManager ;
}
2014-07-25 16:28:47 +02:00
/**
* Return the script manager
*
2017-03-26 13:11:30 +02:00
* @ return \ManiaControl\Script\ScriptManager
2014-07-25 16:28:47 +02:00
*/
public function getScriptManager () {
return $this -> scriptManager ;
}
2014-03-20 16:18:35 +01:00
/**
2014-08-03 01:34:18 +02:00
* Load the server configuration from the config XML
*
* @ return Config
2014-03-20 16:18:35 +01:00
*/
public function loadConfig () {
2014-05-01 20:20:29 +02:00
// Server id parameter
$serverId = CommandLineHelper :: getParameter ( '-id' );
2014-05-02 17:50:30 +02:00
2014-05-27 08:57:26 +02:00
// Server xml element with given id
$serverElement = null ;
2014-05-01 20:20:29 +02:00
if ( $serverId ) {
2014-08-13 11:05:52 +02:00
$serverElements = $this -> maniaControl -> getConfig () -> xpath ( " server[@id=' { $serverId } '] " );
2014-05-27 08:57:26 +02:00
if ( ! $serverElements ) {
2014-06-13 17:11:45 +02:00
$this -> maniaControl -> quit ( " No Server configured with the ID ' { $serverId } '! " , true );
2014-05-01 20:20:29 +02:00
}
2014-05-27 08:57:26 +02:00
$serverElement = $serverElements [ 0 ];
2014-05-02 17:50:30 +02:00
} else {
2014-08-13 11:05:52 +02:00
$serverElements = $this -> maniaControl -> getConfig () -> xpath ( 'server' );
2014-05-27 08:57:26 +02:00
if ( ! $serverElements ) {
2014-06-13 17:11:45 +02:00
$this -> maniaControl -> quit ( 'Invalid server configuration (No Server configured).' , true );
2014-05-01 20:20:29 +02:00
}
2014-05-27 08:57:26 +02:00
$serverElement = $serverElements [ 0 ];
2014-03-20 16:18:35 +01:00
}
2014-05-02 17:50:30 +02:00
2014-06-13 17:24:59 +02:00
// Get config elements
2014-05-27 08:57:26 +02:00
$hostElements = $serverElement -> xpath ( 'host' );
$portElements = $serverElement -> xpath ( 'port' );
2014-06-13 17:11:45 +02:00
$userElements = $serverElement -> xpath ( 'user' );
if ( ! $userElements ) {
$userElements = $serverElement -> xpath ( 'login' );
}
2014-05-27 08:57:26 +02:00
$passElements = $serverElement -> xpath ( 'pass' );
2014-05-02 17:50:30 +02:00
2014-03-20 16:18:35 +01:00
// Create config object
2014-06-20 19:05:59 +02:00
$config = new Config ( $serverId , $hostElements , $portElements , $userElements , $passElements );
2014-06-13 17:24:59 +02:00
$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 );
2014-05-27 08:57:26 +02:00
}
$this -> config = $config ;
2014-08-03 01:34:18 +02:00
return $this -> config ;
2014-03-20 16:18:35 +01:00
}
2014-05-02 17:50:30 +02:00
/**
* Gets all Servers from the Database
*
2014-05-27 23:00:39 +02:00
* @ return \stdClass []
2014-05-02 17:50:30 +02:00
*/
public function getAllServers () {
2014-08-13 11:05:52 +02:00
$mysqli = $this -> maniaControl -> getDatabase () -> getMysqli ();
2014-08-03 01:34:18 +02:00
$query = " SELECT * FROM ` " . self :: TABLE_SERVERS . " `; " ;
2014-05-02 17:50:30 +02:00
$result = $mysqli -> query ( $query );
if ( ! $result ) {
trigger_error ( $mysqli -> error );
return array ();
}
$servers = array ();
while ( $row = $result -> fetch_object ()) {
array_push ( $servers , $row );
}
2014-05-18 23:34:47 +02:00
$result -> free ();
2014-05-02 17:50:30 +02:00
return $servers ;
}
2015-06-16 21:40:56 +02:00
/** Get Server Login by Index
*
* @ param int $index
* @ return string
*/
public function getServerLoginByIndex ( $index ) {
$mysqli = $this -> maniaControl -> getDatabase () -> getMysqli ();
$query = " SELECT * FROM ` " . self :: TABLE_SERVERS . " ` WHERE `index`= " . $index . " ; " ;
$result = $mysqli -> query ( $query );
if ( ! $result ) {
trigger_error ( $mysqli -> error );
return " " ;
}
if ( $result -> num_rows != 1 ) {
return " " ;
}
$row = $result -> fetch_object ();
return $row -> login ;
}
2014-05-02 17:50:30 +02:00
/**
* Handle OnInit Callback
*/
public function onInit () {
$this -> updateProperties ();
}
2014-01-06 15:54:39 +01:00
/**
* Refetch the Server Properties
*/
private function updateProperties () {
// System info
2014-08-13 11:05:52 +02:00
$systemInfo = $this -> maniaControl -> getClient () -> getSystemInfo ();
2014-05-02 17:50:30 +02:00
$this -> ip = $systemInfo -> publishedIp ;
$this -> port = $systemInfo -> port ;
2014-01-16 18:08:32 +01:00
$this -> p2pPort = $systemInfo -> p2PPort ;
2014-05-02 17:50:30 +02:00
$this -> login = $systemInfo -> serverLogin ;
2014-01-16 18:08:32 +01:00
$this -> titleId = $systemInfo -> titleId ;
2014-05-02 17:50:30 +02:00
2014-01-06 15:54:39 +01:00
// Database index
2014-08-13 11:05:52 +02:00
$mysqli = $this -> maniaControl -> getDatabase () -> getMysqli ();
2014-05-02 17:50:30 +02:00
$query = " INSERT INTO ` " . self :: TABLE_SERVERS . " ` (
2014-01-06 15:54:39 +01:00
`login`
) VALUES (
?
) ON DUPLICATE KEY UPDATE
`index` = LAST_INSERT_ID ( `index` ); " ;
$statement = $mysqli -> prepare ( $query );
2014-01-31 16:11:14 +01:00
if ( $mysqli -> error ) {
2014-01-06 15:54:39 +01:00
trigger_error ( $mysqli -> error );
return ;
}
$statement -> bind_param ( 's' , $this -> login );
$statement -> execute ();
2014-01-31 16:11:14 +01:00
if ( $statement -> error ) {
2014-01-06 15:54:39 +01:00
trigger_error ( $statement -> error );
$statement -> close ();
return ;
}
$this -> index = $statement -> insert_id ;
$statement -> close ();
2013-11-09 17:24:03 +01:00
}
2014-01-05 14:41:19 +01:00
/**
2014-02-13 14:21:25 +01:00
* Get Server Player Info
2014-05-02 17:50:30 +02:00
*
2014-05-08 21:30:15 +02:00
* @ return \Maniaplanet\DedicatedServer\Structures\PlayerDetailedInfo
2013-11-09 17:24:03 +01:00
*/
2014-02-13 14:21:25 +01:00
public function getInfo () {
2014-08-13 11:05:52 +02:00
return $this -> maniaControl -> getClient () -> getDetailedPlayerInfo ( $this -> login );
2013-11-09 17:24:03 +01:00
}
/**
2014-01-05 14:41:19 +01:00
* Retrieve Validation Replay for the given Player
2014-05-02 17:50:30 +02:00
*
2014-05-13 16:40:05 +02:00
* @ param string $login
2013-11-10 20:09:08 +01:00
* @ return string
2013-11-09 17:24:03 +01:00
*/
2014-02-20 13:49:33 +01:00
public function getValidationReplay ( $login ) {
2014-05-13 16:40:05 +02:00
$login = Player :: parseLogin ( $login );
2014-01-18 21:46:42 +01:00
try {
2014-08-13 11:05:52 +02:00
$replay = $this -> maniaControl -> getClient () -> getValidationReplay ( $login );
2017-05-24 10:05:28 +02:00
} catch ( Exception $e ) { //UnavailableFeature Exception
2014-08-13 11:05:52 +02:00
$this -> maniaControl -> getErrorHandler () -> triggerDebugNotice ( " Exception line 330 Server.php " . $e -> getMessage ());
2017-05-09 19:28:46 +02:00
Logger :: logError ( " Couldn't get validation replay of ' { $login } '. " . $e -> getMessage ());
2013-11-09 17:24:03 +01:00
return null ;
}
2014-01-16 17:31:15 +01:00
return $replay ;
2013-11-09 17:24:03 +01:00
}
/**
2014-01-05 14:41:19 +01:00
* Retrieve Ghost Replay for the given Player
2014-05-02 17:50:30 +02:00
*
2014-05-13 16:40:05 +02:00
* @ param string $login
2013-11-09 17:24:03 +01:00
* @ return string
*/
2014-02-20 13:49:33 +01:00
public function getGhostReplay ( $login ) {
2017-05-15 19:30:22 +02:00
$dataDir = $this -> getDirectory () -> getUserDataFolder ();
2014-01-31 16:11:14 +01:00
if ( ! $this -> checkAccess ( $dataDir )) {
2014-01-08 20:11:48 +01:00
return null ;
}
2014-05-02 17:50:30 +02:00
2013-11-09 17:24:03 +01:00
// Build file name
2014-05-13 16:40:05 +02:00
$login = Player :: parseLogin ( $login );
2014-08-13 11:05:52 +02:00
$map = $this -> maniaControl -> getMapManager () -> getCurrentMap ();
2014-05-11 16:02:29 +02:00
$gameMode = $this -> getGameMode ();
2014-05-02 17:50:30 +02:00
$time = time ();
2014-02-20 13:49:33 +01:00
$fileName = " GhostReplays/Ghost. { $login } . { $gameMode } . { $time } . { $map -> uid } .Replay.Gbx " ;
2014-05-02 17:50:30 +02:00
2013-11-09 17:24:03 +01:00
// Save ghost replay
2014-01-18 21:46:42 +01:00
try {
2014-08-13 11:05:52 +02:00
$this -> maniaControl -> getClient () -> saveBestGhostsReplay ( $login , $fileName );
2014-05-02 17:50:30 +02:00
} catch ( Exception $e ) {
2014-05-01 20:20:29 +02:00
// TODO temp added 19.04.2014
2014-08-13 11:05:52 +02:00
$this -> maniaControl -> getErrorHandler () -> triggerDebugNotice ( " Exception line 360 Server.php " . $e -> getMessage ());
2014-05-02 17:50:30 +02:00
2014-01-18 21:46:42 +01:00
trigger_error ( " Couldn't save ghost replay. " . $e -> getMessage ());
2013-11-09 17:24:03 +01:00
return null ;
}
2014-05-02 17:50:30 +02:00
2013-11-09 17:24:03 +01:00
// Load replay file
2014-01-06 15:54:39 +01:00
$ghostReplay = file_get_contents ( " { $dataDir } Replays/ { $fileName } " );
2014-01-31 16:11:14 +01:00
if ( ! $ghostReplay ) {
2013-11-09 17:24:03 +01:00
trigger_error ( " Couldn't retrieve saved ghost replay. " );
return null ;
}
return $ghostReplay ;
}
2014-10-24 20:08:21 +02:00
/**
* Return the server directory
*
* @ return Directory
*/
public function getDirectory () {
return $this -> directory ;
}
2014-05-02 17:50:30 +02:00
/**
2014-06-14 11:32:09 +02:00
* Check if ManiaControl has Access to the given Directory
2014-05-02 17:50:30 +02:00
*
* @ param string $directory
* @ return bool
*/
public function checkAccess ( $directory ) {
return ( is_dir ( $directory ) && is_writable ( $directory ));
}
2014-05-11 16:02:29 +02:00
/**
* 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 {
2014-08-13 11:05:52 +02:00
$gameMode = $this -> maniaControl -> getClient () -> getGameMode ();
2014-05-11 16:02:29 +02:00
}
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 ;
}
2013-11-09 17:24:03 +01:00
/**
2014-01-05 14:41:19 +01:00
* Wait for the Server to have the given Status
2014-05-02 17:50:30 +02:00
*
2014-01-03 20:57:24 +01:00
* @ param int $statusCode
2013-11-10 20:09:08 +01:00
* @ return bool
2013-11-09 17:24:03 +01:00
*/
2013-11-10 20:09:08 +01:00
public function waitForStatus ( $statusCode = 4 ) {
2014-08-13 11:05:52 +02:00
$response = $this -> maniaControl -> getClient () -> getStatus ();
2013-11-10 20:09:08 +01:00
// Check if server has the given status
2014-01-31 16:11:14 +01:00
if ( $response -> code === 4 ) {
2014-01-08 20:11:48 +01:00
return true ;
}
2014-01-06 15:54:39 +01:00
// Server not yet in given status - Wait for it...
2014-05-02 17:50:30 +02:00
$waitBegin = time ();
2014-02-27 15:03:01 +01:00
$maxWaitTime = 50 ;
2014-05-02 17:50:30 +02:00
$lastStatus = $response -> name ;
2014-08-05 01:49:13 +02:00
Logger :: log ( " Waiting for server to reach status { $statusCode } ... " );
Logger :: log ( " Current Status: { $lastStatus } " );
2014-05-01 20:20:29 +02:00
while ( $response -> code !== 4 ) {
2013-11-09 17:24:03 +01:00
sleep ( 1 );
2014-08-13 11:05:52 +02:00
$response = $this -> maniaControl -> getClient () -> getStatus ();
2014-02-15 21:40:42 +01:00
if ( $lastStatus !== $response -> name ) {
2014-08-05 01:49:13 +02:00
Logger :: log ( " New Status: { $response -> name } " );
2014-02-15 21:40:42 +01:00
$lastStatus = $response -> name ;
2013-11-09 17:24:03 +01:00
}
2014-01-31 16:11:14 +01:00
if ( time () - $maxWaitTime > $waitBegin ) {
2013-11-09 17:24:03 +01:00
// It took too long to reach the status
2014-08-05 01:49:13 +02:00
Logger :: logError ( " Server couldn't reach status { $statusCode } after { $maxWaitTime } seconds! " );
2013-11-09 17:24:03 +01:00
return false ;
}
}
return true ;
}
2014-05-11 15:28:53 +02:00
/**
* Set whether the Server Runs a Team - Based Mode or not
*
* @ param bool $teamMode
*/
public function setTeamMode ( $teamMode = true ) {
2014-05-11 16:02:29 +02:00
$oldStatus = $this -> teamMode ;
2014-10-24 20:08:21 +02:00
$this -> teamMode = ( bool ) $teamMode ;
2014-05-11 16:02:29 +02:00
// Trigger callback
if ( $oldStatus !== $this -> teamMode | $oldStatus === null ) {
2014-08-13 11:05:52 +02:00
$this -> maniaControl -> getCallbackManager () -> triggerCallback ( self :: CB_TEAM_MODE_CHANGED , $teamMode );
2014-05-11 16:02:29 +02:00
}
2014-05-11 15:28:53 +02:00
}
/**
* Check if the Server Runs a Team - Based Mode
*
2017-04-13 20:46:23 +02:00
* @ deprecated
* @ see ScriptManager :: modeIsTeamMode ()
2014-05-11 15:28:53 +02:00
* @ return bool
*/
public function isTeamMode () {
2014-05-11 16:02:29 +02:00
return $this -> teamMode ;
2014-05-11 15:28:53 +02:00
}
2014-06-10 23:29:28 +02:00
2014-08-03 01:34:18 +02:00
/**
* Build the join link
*
* @ return string
*/
public function getJoinLink () {
return 'maniaplanet://#join=' . $this -> login . '@' . $this -> titleId ;
}
2014-06-10 23:29:28 +02:00
/**
* Check if the Servers is empty
*
* @ return bool
*/
public function isEmpty () {
2014-08-13 11:05:52 +02:00
return ( $this -> maniaControl -> getPlayerManager () -> getPlayerCount ( false ) === 0 );
2014-06-10 23:29:28 +02:00
}
2013-11-09 17:24:03 +01:00
}