diff --git a/src/EntityOLD.php b/src/EntityOLD.php index 86832a7ec..53071561f 100644 --- a/src/EntityOLD.php +++ b/src/EntityOLD.php @@ -69,7 +69,7 @@ function __construct(Level $level, $eid, $class, $type = 0, $data = array()){ $this->level = $level; $this->fallY = false; $this->fallStart = false; -$this->server = ServerAPI::request(); +$this->server = Server::getInstance(); $this->eid = (int) $eid; $this->type = (int) $type; $this->class = (int) $class; diff --git a/src/PocketMine/Achievement.php b/src/PocketMine/Achievement.php index 4ec6e887e..c0f082bef 100644 --- a/src/PocketMine/Achievement.php +++ b/src/PocketMine/Achievement.php @@ -104,10 +104,10 @@ abstract class Achievement{ public static function broadcast(Player $player, $achievementId){ if(isset(Achievement::$list[$achievementId])){ - if(ServerAPI::request()->api->getProperty("announce-player-achievements") == true){ - Player::broadcastChat($player->getUsername() . " has just earned the achievement " . Achievement::$list[$achievementId]["name"]); + if(Server::getInstance()->api->getProperty("announce-player-achievements") == true){ + Player::broadcastMessage($player->getDisplayName() . " has just earned the achievement " . Achievement::$list[$achievementId]["name"]); }else{ - $player->sendChat("You have just earned the achievement " . Achievement::$list[$achievementId]["name"]); + $player->sendMessage("You have just earned the achievement " . Achievement::$list[$achievementId]["name"]); } return true; diff --git a/src/PocketMine/BanAPI.php b/src/PocketMine/BanAPI.php index 4340bb0b6..06d07fbc8 100644 --- a/src/PocketMine/BanAPI.php +++ b/src/PocketMine/BanAPI.php @@ -40,7 +40,7 @@ class BanAPI{ private $bannedIPs; private $cmdWhitelist = array(); //Command WhiteList function __construct(){ - $this->server = ServerAPI::request(); + $this->server = Server::getInstance(); } public function init(){ @@ -97,13 +97,13 @@ class BanAPI{ public function permissionsCheck($data, $event){ switch($event){ case "player.flying": //OPs can fly around the server. - if($this->isOp($data->getUsername())){ + if($this->isOp($data->getName())){ return true; } break; case "player.block.break": case "player.block.place": //Spawn protection detection. Allows OPs to place/break blocks in the spawn area. - if(!$this->isOp($data["player"]->getUsername())){ + if(!$this->isOp($data["player"]->getName())){ $t = new Vector2($data["target"]->x, $data["target"]->z); $s = new Vector2(Level::getDefault()->getSpawn()->x, Level::getDefault()->getSpawn()->z); if($t->distance($s) <= $this->server->api->getProperty("spawn-protection") and $this->server->api->dhandle($event . ".spawn", $data) !== true){ @@ -118,7 +118,7 @@ class BanAPI{ } if($data["issuer"] instanceof Player){ - if($this->server->api->handle("console.check", $data) === true or $this->isOp($data["issuer"]->getUsername())){ + if($this->server->api->handle("console.check", $data) === true or $this->isOp($data["issuer"]->getName())){ return; } }elseif($data["issuer"] === "console" or $data["issuer"] === "rcon"){ @@ -148,7 +148,7 @@ class BanAPI{ break; } $this->server->api->console->run(implode(" ", $params), $player); - $output .= "Command ran as " . $player->getUsername() . ".\n"; + $output .= "Command ran as " . $player->getName() . ".\n"; break; case "op": $user = strtolower($params[0]); @@ -163,10 +163,10 @@ class BanAPI{ $output .= $user . " is now op\n"; break; } - $this->ops->set(strtolower($player->getUsername())); + $this->ops->set(strtolower($player->getName())); $this->ops->save(); - $output .= $player->getUsername() . " is now op\n"; - $player->sendChat("You are now op."); + $output .= $player->getName() . " is now op\n"; + $player->sendMessage("You are now op."); break; case "deop": $user = strtolower($params[0]); @@ -177,10 +177,10 @@ class BanAPI{ $output .= $user . " is no longer op\n"; break; } - $this->ops->remove(strtolower($player->getUsername())); + $this->ops->remove(strtolower($player->getName())); $this->ops->save(); - $output .= $player->getUsername() . " is no longer op\n"; - $player->sendChat("You are no longer op."); + $output .= $player->getName() . " is no longer op\n"; + $player->sendMessage("You are no longer op."); break; case "kick": if(!isset($params[0])){ @@ -197,9 +197,9 @@ class BanAPI{ $this->server->schedule(60, array($player, "close"), "You have been kicked: " . $reason); //Forces a kick $player->blocked = true; if($issuer instanceof Player){ - Player::broadcastChat($player->getUsername() . " has been kicked by " . $issuer->getUsername() . ": $reason\n"); + Player::broadcastMessage($player->getName() . " has been kicked by " . $issuer->getName() . ": $reason\n"); }else{ - Player::broadcastChat($player->getUsername() . " has been kicked: $reason\n"); + Player::broadcastMessage($player->getName() . " has been kicked: $reason\n"); } } } @@ -295,9 +295,9 @@ class BanAPI{ $player->kick("You are banned"); } if($issuer instanceof Player){ - Player::broadcastChat($user . " has been banned by " . $issuer->getUsername() . "\n"); + Player::broadcastMessage($user . " has been banned by " . $issuer->getName() . "\n"); }else{ - Player::broadcastChat($user . " has been banned\n"); + Player::broadcastMessage($user . " has been banned\n"); } $this->kick($user, "Banned"); $output .= "Player \"$user\" added to ban list\n"; diff --git a/src/PocketMine/BlockAPI.php b/src/PocketMine/BlockAPI.php index 6e3b99673..0635fa2b3 100644 --- a/src/PocketMine/BlockAPI.php +++ b/src/PocketMine/BlockAPI.php @@ -227,7 +227,7 @@ class BlockAPI{ ); function __construct(){ - $this->server = ServerAPI::request(); + $this->server = Server::getInstance(); } public function init(){ @@ -262,7 +262,7 @@ class BlockAPI{ break; } $player->addItem(clone $item); - $output .= "Giving " . $item->getCount() . " of " . $item->getName() . " (" . $item->getID() . ":" . $item->getMetadata() . ") to " . $player->getUsername() . "\n"; + $output .= "Giving " . $item->getCount() . " of " . $item->getName() . " (" . $item->getID() . ":" . $item->getMetadata() . ") to " . $player->getName() . "\n"; }else{ $output .= "Unknown player.\n"; } diff --git a/src/PocketMine/ChatAPI.php b/src/PocketMine/ChatAPI.php index dcc5cce94..47f76dd92 100644 --- a/src/PocketMine/ChatAPI.php +++ b/src/PocketMine/ChatAPI.php @@ -29,7 +29,7 @@ class ChatAPI{ private $server; function __construct(){ - $this->server = ServerAPI::request(); + $this->server = Server::getInstance(); } public function init(){ @@ -59,7 +59,7 @@ class ChatAPI{ break; } $sender = ($issuer instanceof Player) ? "Server" : ucfirst($issuer); - Player::broadcastChat("[$sender] " . $s); + Player::broadcastMessage("[$sender] " . $s); break; case "me": if(!($issuer instanceof Player)){ @@ -69,9 +69,9 @@ class ChatAPI{ $sender = ucfirst($issuer); } }else{ - $sender = $issuer->getUsername(); + $sender = $issuer->getDisplayName(); } - Player::broadcastChat("* $sender " . implode(" ", $params)); + Player::broadcastMessage("* $sender " . implode(" ", $params)); break; case "tell": if(!isset($params[0]) or !isset($params[1])){ @@ -92,12 +92,12 @@ class ChatAPI{ } } $mes = implode(" ", $params); - $output .= "[me -> " . ($target instanceof Player ? $target->getUsername() : $target) . "] " . $mes . "\n"; + $output .= "[me -> " . ($target instanceof Player ? $target->getDisplayName() : $target) . "] " . $mes . "\n"; if($target instanceof Player){ - $target->sendChat("[" . ($sender instanceof Player ? $sender->getUsername() : $sender) . " -> me] " . $mes); + $target->sendMessage("[" . ($sender instanceof Player ? $sender->getDisplayName() : $sender) . " -> me] " . $mes); } if($target === "Console" or $sender === "Console"){ - console("[INFO] [" . ($sender instanceof Player ? $sender->getUsername() : $sender) . " -> " . ($target instanceof Player ? $target->getUsername() : $target) . "] " . $mes); + console("[INFO] [" . ($sender instanceof Player ? $sender->getDisplayName() : $sender) . " -> " . ($target instanceof Player ? $target->getDisplayName() : $target) . "] " . $mes); } break; } diff --git a/src/PocketMine/CommandReader.php b/src/PocketMine/CommandReader.php new file mode 100644 index 000000000..fe3d18d26 --- /dev/null +++ b/src/PocketMine/CommandReader.php @@ -0,0 +1,95 @@ +stream = $stream; + $this->start(PTHREADS_INHERIT_ALL & ~PTHREADS_INHERIT_CLASSES); + } + + private function readLine(){ + if(!$this->readline){ + $line = trim(fgets($this->fp)); + }else{ + $line = trim(readline("> ")); + if($line != ""){ + readline_add_history($line); + } + } + + return $line; + } + + /** + * Reads a line from console, if available. Returns null if not available + * + * @return string|null + */ + public function getLine(){ + if($this->buffer->count() !== 0){ + return $this->buffer->synchronized(function(){ + return $this->buffer->shift(); + }); + } + return null; + } + + public function run(){ + $this->buffer = new \Threaded; + if(extension_loaded("readline") and $this->stream === "php://stdin"){ + $this->readline = true; + }else{ + $this->readline = false; + $this->fp = fopen($this->stream, "r"); + stream_set_blocking($this->fp, 0); //Won't work on Windows :P + } + + $lastLine = microtime(true); + while(true){ + if(($line = $this->readLine()) !== ""){ + $this->buffer->synchronized(function(\Threaded $buffer, $line){ + $buffer[] = $line; + }, $this->buffer, $line); + $lastLine = microtime(true); + }elseif((microtime(true) - $lastLine) <= 0.1){ //Non blocking! Sleep to save CPU + usleep(40000); + } + } + } +} \ No newline at end of file diff --git a/src/PocketMine/ConsoleAPI.php b/src/PocketMine/ConsoleAPI.php index 865bcc7db..6ed71226e 100644 --- a/src/PocketMine/ConsoleAPI.php +++ b/src/PocketMine/ConsoleAPI.php @@ -31,7 +31,7 @@ class ConsoleAPI{ $this->help = array(); $this->cmds = array(); $this->alias = array(); - $this->server = ServerAPI::request(); + $this->server = Server::getInstance(); } public function init(){ @@ -185,7 +185,7 @@ class ConsoleAPI{ return $this->run($this->alias[$cmd] . ($params !== "" ? " " . $params : ""), $issuer, $cmd); } if($issuer instanceof Player){ - console("[DEBUG] " . TextFormat::AQUA . $issuer->getUsername() . TextFormat::RESET . " issued server command: " . ltrim("$alias ") . "/$cmd " . $params, true, true, 2); + console("[DEBUG] " . TextFormat::AQUA . $issuer->getName() . TextFormat::RESET . " issued server command: " . ltrim("$alias ") . "/$cmd " . $params, true, true, 2); }else{ console("[DEBUG] " . TextFormat::YELLOW . "*" . $issuer . TextFormat::RESET . " issued server command: " . ltrim("$alias ") . "/$cmd " . $params, true, true, 2); } @@ -202,7 +202,7 @@ class ConsoleAPI{ case "u": case "player": case "username": - $p = ($issuer instanceof Player) ? $issuer->getUsername() : $issuer; + $p = ($issuer instanceof Player) ? $issuer->getName() : $issuer; $params = substr_replace($params, $p, $selector[1] + $offsetshift - 1, strlen($selector[0]) + 1); $offsetshift -= strlen($selector[0]) - strlen($p) + 1; break; @@ -215,18 +215,18 @@ class ConsoleAPI{ case "a": case "all": if($issuer instanceof Player){ - if($this->server->api->ban->isOp($issuer->getUsername())){ + if($this->server->api->ban->isOp($issuer->getName())){ $output = ""; foreach(Player::getAll() as $p){ - $output .= $this->run($cmd . " " . substr_replace($params, $p->getUsername(), $selector[1] + $offsetshift - 1, strlen($selector[0]) + 1), $issuer, $alias); + $output .= $this->run($cmd . " " . substr_replace($params, $p->getName(), $selector[1] + $offsetshift - 1, strlen($selector[0]) + 1), $issuer, $alias); } }else{ - $issuer->sendChat("You don't have permissions to use this command.\n"); + $issuer->sendMessage("You don't have permissions to use this command.\n"); } }else{ $output = ""; foreach(Player::getAll() as $p){ - $output .= $this->run($cmd . " " . substr_replace($params, $p->getUsername(), $selector[1] + $offsetshift - 1, strlen($selector[0]) + 1), $issuer, $alias); + $output .= $this->run($cmd . " " . substr_replace($params, $p->getName(), $selector[1] + $offsetshift - 1, strlen($selector[0]) + 1), $issuer, $alias); } } @@ -243,7 +243,7 @@ class ConsoleAPI{ return; } - $p = $l[mt_rand(0, count($l) - 1)]->getUsername(); + $p = $l[mt_rand(0, count($l) - 1)]->getName(); $params = substr_replace($params, $p, $selector[1] + $offsetshift - 1, strlen($selector[0]) + 1); $offsetshift -= strlen($selector[0]) - strlen($p) + 1; break; @@ -268,7 +268,7 @@ class ConsoleAPI{ } if($output != "" and ($issuer instanceof Player)){ - $issuer->sendChat(trim($output)); + $issuer->sendMessage(trim($output)); } return $output; diff --git a/src/PocketMine/LevelAPI.php b/src/PocketMine/LevelAPI.php index 1b1e08c30..bf34d75bf 100644 --- a/src/PocketMine/LevelAPI.php +++ b/src/PocketMine/LevelAPI.php @@ -27,7 +27,7 @@ class LevelAPI{ private $server; public function __construct(){ - $this->server = ServerAPI::request(); + $this->server = Server::getInstance(); } public function init(){ diff --git a/src/PocketMine/Player.php b/src/PocketMine/Player.php index c3e9305df..6641b74c0 100644 --- a/src/PocketMine/Player.php +++ b/src/PocketMine/Player.php @@ -37,6 +37,7 @@ use PocketMine\NBT\Tag\Float; use PocketMine\NBT\Tag\Int; use PocketMine\NBT\Tag\Short; use PocketMine\NBT\Tag\String; +use PocketMine\Command\CommandSender; use PocketMine\Network\Protocol\AdventureSettingsPacket; use PocketMine\Network\Protocol\AnimatePacket; use PocketMine\Network\Protocol\ChunkDataPacket; @@ -80,11 +81,25 @@ use PocketMine\Utils\Utils; * Class Player * @package PocketMine */ -class Player extends RealHuman{ +class Player extends RealHuman/*TODO: implements CommandSender*/{ + const BROADCAST_CHANNEL_ADMINISTRATIVE = "pocketmine.broadcast.admin"; + const BROADCAST_CHANNEL_USERS = "pocketmine.broadcast.user"; + + const SURVIVAL = 0; + const CREATIVE = 1; + const ADVENTURE = 2; + const SPECTATOR = 3; + const VIEW = Player::SPECTATOR; + const MAX_QUEUE = 2048; const SURVIVAL_SLOTS = 36; const CREATIVE_SLOTS = 112; - public static $list = array(); //???? + + /** + * @var Player[] + */ + public static $list = array(); + public $auth = false; public $CID; public $MTU; @@ -110,6 +125,7 @@ class Player extends RealHuman{ protected $port; protected $username; protected $iusername; + protected $displayName; protected $startAction = false; protected $sleeping = false; protected $chunksOrder = array(); @@ -149,7 +165,7 @@ class Player extends RealHuman{ public function __construct($clientID, $ip, $port, $MTU){ $this->bigCnt = 0; $this->MTU = $MTU; - $this->server = ServerAPI::request(); + $this->server = Server::getInstance(); $this->lastBreak = microtime(true); $this->clientID = $clientID; $this->CID = Server::clientID($ip, $port); @@ -160,7 +176,7 @@ class Player extends RealHuman{ $this->timeout = microtime(true) + 20; $this->gamemode = $this->server->gamemode; $this->level = Level::getDefault(); - $this->viewDistance = (int) $this->server->api->getProperty("view-distance"); + $this->viewDistance = $this->server->getViewDistance(); $this->slot = 0; $this->hotbar = array(0, -1, -1, -1, -1, -1, -1, -1, -1); $this->packetStats = array(0, 0); @@ -196,6 +212,14 @@ class Player extends RealHuman{ return $this->connected === true; } + public function getDisplayName(){ + return $this->displayName; + } + + public function setDisplayName($name){ + $this->displayName = $name; + } + public function getIP(){ return $this->ip; } @@ -663,11 +687,11 @@ class Player extends RealHuman{ if(($this->gamemode & 0x01) === ($gm & 0x01)){ $this->gamemode = $gm; - $this->sendChat("Your gamemode has been changed to " . $this->getGamemode() . ".\n"); + $this->sendMessage("Your gamemode has been changed to " . $this->getGamemode() . ".\n"); }else{ $this->blocked = true; $this->gamemode = $gm; - $this->sendChat("Your gamemode has been changed to " . $this->getGamemode() . ", you've to do a forced reconnect.\n"); + $this->sendMessage("Your gamemode has been changed to " . $this->getGamemode() . ", you've to do a forced reconnect.\n"); $this->server->schedule(30, array($this, "close"), "gamemode change"); //Forces a kick } $this->sendSettings(); @@ -969,7 +993,7 @@ class Player extends RealHuman{ if(count($u) > 0){ foreach($u as $p){ if($p !== $this){ - $p->close($p->getUsername() . " has left the game", "logged in from another location"); + $p->close($p->getDisplayName() . " has left the game", "logged in from another location"); } } } @@ -1068,7 +1092,7 @@ class Player extends RealHuman{ //$this->heal($this->data->get("health"), "spawn", true); $this->spawned = true; $this->spawnToAll(); - $this->sendChat($this->server->motd . "\n"); + $this->sendMessage($this->server->motd . "\n"); $this->sendInventory(); $this->sendSettings(); @@ -1608,7 +1632,7 @@ class Player extends RealHuman{ }else{ $ev = new Event\Player\PlayerChatEvent($this, $message); if(EventHandler::callEvent($ev) !== Event\Event::DENY){ - Player::groupChat(sprintf($ev->getFormat(), $ev->getPlayer()->getUsername(), $ev->getMessage()), $ev->getRecipients()); + Player::groupChat(sprintf($ev->getFormat(), $ev->getPlayer()->getDisplayName(), $ev->getMessage()), $ev->getRecipients()); } } } @@ -1865,7 +1889,7 @@ class Player extends RealHuman{ */ public function kick($reason = ""){ if(EventHandler::callEvent($ev = new Event\Player\PlayerKickEvent($this, $reason, "Kicked player " . $this->username . "." . ($reason !== "" ? " With reason: $reason" : ""))) !== Event\Event::DENY){ - $this->sendChat("You have been kicked. " . ($reason !== "" ? " Reason: $reason" : "") . "\n"); + $this->sendMessage("You have been kicked. " . ($reason !== "" ? " Reason: $reason" : "") . "\n"); $this->close($ev->getQuitMessage(), $reason); return true; @@ -1879,7 +1903,7 @@ class Player extends RealHuman{ * * @param string $message */ - public function sendChat($message){ + public function sendMessage($message){ $mes = explode("\n", $message); foreach($mes as $m){ if(preg_match_all('#@([@A-Za-z_]{1,})#', $m, $matches, PREG_OFFSET_CAPTURE) > 0){ @@ -1934,7 +1958,7 @@ class Player extends RealHuman{ Player::saveOffline($this->username, $this->namedtag); } if(isset($ev) and $this->username != "" and $this->spawned !== false and $ev->getQuitMessage() != ""){ - Player::broadcastChat($ev->getQuitMessage()); + Player::broadcastMessage($ev->getQuitMessage()); } $this->spawned = false; console("[INFO] " . TextFormat::AQUA . $this->username . TextFormat::RESET . "[/" . $this->ip . ":" . $this->port . "] logged out due to " . $reason); @@ -2007,7 +2031,7 @@ class Player extends RealHuman{ * * @param string $message */ - public static function broadcastChat($message){ + public static function broadcastMessage($message){ self::groupChat($message, self::getAll()); } @@ -2060,7 +2084,7 @@ class Player extends RealHuman{ * @return Compound */ public static function getOffline($name){ - $server = ServerAPI::request(); + $server = Server::getInstance(); $iname = strtolower($name); if(!file_exists(\PocketMine\DATA . "players/" . $iname . ".dat")){ $spawn = Level::getDefault()->getSafeSpawn(); @@ -2182,7 +2206,7 @@ class Player extends RealHuman{ */ public static function groupChat($message, array $players){ foreach($players as $p){ - $p->sendChat($message); + $p->sendMessage($message); } } @@ -2191,7 +2215,7 @@ class Player extends RealHuman{ * * @return string */ - public function getUsername(){ + public function getName(){ return $this->username; } diff --git a/src/PocketMine/PlayerAPI.php b/src/PocketMine/PlayerAPI.php index 47ab26dc1..1cdbc9039 100644 --- a/src/PocketMine/PlayerAPI.php +++ b/src/PocketMine/PlayerAPI.php @@ -30,7 +30,7 @@ class PlayerAPI{ private $server; function __construct(){ - $this->server = ServerAPI::request(); + $this->server = Server::getInstance(); } public function init(){ @@ -116,7 +116,7 @@ class PlayerAPI{ break; } } - Player::broadcastChat($data["player"]->getUsername() . $message); + Player::broadcastMessage($data["player"]->getName() . $message); return true; } @@ -166,7 +166,7 @@ class PlayerAPI{ if($target instanceof Level){ $output .= "Spawnpoint of world " . $target->getName() . " set correctly!\n"; }elseif($target !== $issuer){ - $output .= "Spawnpoint of " . $target->getUsername() . " set correctly!\n"; + $output .= "Spawnpoint of " . $target->getName() . " set correctly!\n"; }else{ $output .= "Spawnpoint set correctly!\n"; } @@ -229,13 +229,13 @@ class PlayerAPI{ break; } if($player->setGamemode($gms[strtolower($setgm)])){ - $output .= "Gamemode of " . $player->getUsername() . " changed to " . $player->getGamemode() . "\n"; + $output .= "Gamemode of " . $player->getName() . " changed to " . $player->getGamemode() . "\n"; } break; case "tp": if(count($params) <= 2 or substr($params[0], 0, 2) === "w:" or substr($params[1], 0, 2) === "w:"){ if((!isset($params[1]) or substr($params[0], 0, 2) === "w:") and isset($params[0]) and ($issuer instanceof Player)){ - $name = $issuer->getUsername(); + $name = $issuer->getName(); $target = implode(" ", $params); }elseif(isset($params[1]) and isset($params[0])){ $name = array_shift($params); @@ -251,7 +251,7 @@ class PlayerAPI{ } }else{ if(!isset($params[3]) and isset($params[2]) and isset($params[1]) and isset($params[0]) and ($issuer instanceof Player)){ - $name = $issuer->getUsername(); + $name = $issuer->getName(); $x = $params[0]; $y = $params[1]; $z = $params[2]; @@ -280,7 +280,7 @@ class PlayerAPI{ } if($player instanceof Player){ $player->harm(1000, "console", true); - $player->sendChat("Ouch. That looks like it hurt.\n"); + $player->sendMessage("Ouch. That looks like it hurt.\n"); }else{ $output .= "Usage: /$cmd [player]\n"; } @@ -291,7 +291,7 @@ class PlayerAPI{ break; } foreach(Player::$list as $c){ - $output .= $c->getUsername() . ", "; + $output .= $c->getName() . ", "; } $output = substr($output, 0, -2) . "\n"; break; @@ -306,7 +306,7 @@ class PlayerAPI{ if($lv instanceof Level){ $origin = Player::get($name); if($origin instanceof Player){ - $name = $origin->getUsername(); + $name = $origin->getName(); return $origin->teleport($lv->getSafeSpawn()); } @@ -316,10 +316,10 @@ class PlayerAPI{ } $player = Player::get($target); if($player instanceof Player and $player->spawned === true){ - $target = $player->getUsername(); + $target = $player->getName(); $origin = Player::get($name); if($origin instanceof Player){ - $name = $origin->getUsername(); + $name = $origin->getName(); return $origin->teleport($player->entity); } @@ -331,7 +331,7 @@ class PlayerAPI{ public function tppos(&$name, &$x, &$y, &$z){ $player = Player::get($name); if($player instanceof Player and $player->spawned === true){ - $name = $player->getUsername(); + $name = $player->getName(); $x = $x{0} === "~" ? $player->x + floatval(substr($x, 1)) : floatval($x); $y = $y{0} === "~" ? $player->y + floatval(substr($y, 1)) : floatval($y); $z = $z{0} === "~" ? $player->z + floatval(substr($z, 1)) : floatval($z); diff --git a/src/PocketMine/PluginAPI.php b/src/PocketMine/PluginAPI.php index 33a4dd9bd..b54345799 100644 --- a/src/PocketMine/PluginAPI.php +++ b/src/PocketMine/PluginAPI.php @@ -28,7 +28,7 @@ class PluginAPI extends \stdClass{ private $server; public function __construct(){ - $this->server = ServerAPI::request(); + $this->server = Server::getInstance(); $this->server->api->console->register("plugins", "", array($this, "commandHandler")); $this->server->api->console->register("version", "", array($this, "commandHandler")); $this->server->api->ban->cmdWhitelist("version"); @@ -39,19 +39,12 @@ class PluginAPI extends \stdClass{ switch($cmd){ case "plugins": $output = "Plugins: "; - foreach(PluginManager::getPlugins() as $plugin){ + foreach($this->server->getPluginManager()->getPlugins() as $plugin){ $d = $plugin->getDescription(); $output .= $d->getName() . ": " . $d->getVersion() . ", "; } $output = $output === "Plugins: " ? "No plugins installed.\n" : substr($output, 0, -2) . "\n"; break; - case "version": - $output = "PocketMine-MP " . \PocketMine\VERSION . " 「" . \PocketMine\CODENAME . "」 API #" . \PocketMine\API_VERSION . " for Minecraft: PE " . \PocketMine\MINECRAFT_VERSION . " protocol #" . Info::CURRENT_PROTOCOL; - if(\PocketMine\GIT_COMMIT !== str_repeat("00", 20)){ - $output .= " (git " . \PocketMine\GIT_COMMIT . ")"; - } - $output .= "\n"; - break; } return $output; diff --git a/src/PocketMine/PocketMine.php b/src/PocketMine/PocketMine.php index 778a614f2..a22ff8cc3 100644 --- a/src/PocketMine/PocketMine.php +++ b/src/PocketMine/PocketMine.php @@ -23,10 +23,10 @@ namespace { /** * Output text to the console, can contain Minecraft-formatted text. * - * @param $message - * @param bool $EOL - * @param bool $log - * @param int $level + * @param string $message + * @param bool $EOL + * @param bool $log + * @param int $level */ function console($message, $EOL = true, $log = true, $level = 1){ PocketMine\console($message, $EOL, $log, $level); @@ -156,8 +156,10 @@ namespace PocketMine { ini_set("memory_limit", "128M"); //Default define("PocketMine\\START_TIME", microtime(true)); - $opts = getopt("", array("enable-ansi", "disable-ansi", "data-path:", "no-wizard")); - define("PocketMine\\DATA", isset($opts["data-path"]) ? realpath($opts["data-path"]) . DIRECTORY_SEPARATOR : \PocketMine\PATH); + $opts = getopt("", array("enable-ansi", "disable-ansi", "data:", "plugins:", "no-wizard")); + + define("PocketMine\\DATA", isset($opts["data"]) ? realpath($opts["data"]) . DIRECTORY_SEPARATOR : \PocketMine\PATH); + define("PocketMine\\PLUGIN_PATH", isset($opts["plugins"]) ? realpath($opts["plugins"]) . DIRECTORY_SEPARATOR : \PocketMine\PATH . "plugins/"); if((strpos(strtoupper(php_uname("s")), "WIN") === false or isset($opts["enable-ansi"])) and !isset($opts["disable-ansi"])){ define("PocketMine\\ANSI", true); @@ -373,7 +375,6 @@ namespace PocketMine { exit(1); //Exit with error } - $gitsha1 = false; if(file_exists(\PocketMine\PATH . ".git/refs/heads/master")){ //Found Git information! define("PocketMine\\GIT_COMMIT", strtolower(trim(file_get_contents(\PocketMine\PATH . ".git/refs/heads/master")))); }else{ //Unknown :( @@ -387,7 +388,7 @@ namespace PocketMine { } if(!defined("PARENT_API_EXISTENT")){ - $server = new Server(); + $server = new Server(\PocketMine\PATH, \PocketMine\DATA, \PocketMine\PLUGIN_PATH); $server->start(); kill(getmypid()); //Fix for ConsoleAPI being blocked diff --git a/src/PocketMine/Server.php b/src/PocketMine/Server.php index d0a5d545a..43752a317 100644 --- a/src/PocketMine/Server.php +++ b/src/PocketMine/Server.php @@ -2,11 +2,11 @@ /* * - * ____ _ _ __ __ _ __ __ ____ - * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \ + * ____ _ _ __ __ _ __ __ ____ + * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \ * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) | - * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/ - * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_| + * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/ + * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_| * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -15,7 +15,7 @@ * * @author PocketMine Team * @link http://www.pocketmine.net/ - * + * * */ @@ -25,662 +25,626 @@ */ namespace PocketMine; -use PocketMine\Entity\Entity; -use PocketMine\Network\Handler; use PocketMine\Network\Packet; -use PocketMine\Network\Protocol\Info; -use PocketMine\Network\RakNet\Info as RakNetInfo; -use PocketMine\Network\RakNet\Packet as RakNetPacket; -use PocketMine\Plugin\PluginManager; +use PocketMine\Utils\Config; use PocketMine\Utils\Utils; use PocketMine\Utils\VersionString; +use PocketMine\Network\UPnP\UPnP; +use PocketMine\Utils\TextFormat; +use PocketMine\Plugin\PluginManager; +use PocketMine\Tile\Tile; +use PocketMine\Entity\Entity; +use PocketMine\Level\Level; +use PocketMine\Block\Block; +use PocketMine\Item\Item; +use PocketMine\Recipes\Crafting; +use PocketMine\Network\Handler; +use PocketMine\Scheduler\ServerScheduler; +use PocketMine\Scheduler\TickScheduler; +use PocketMine\Command\SimpleCommandMap; +use PocketMine\Command\ConsoleCommandSender; class Server{ - public $tCnt; - public $serverID, $interface, $database, $version, $invisible, $tickMeasure, $preparedSQL, $spawn, $whitelist, $seed, $stop, $gamemode, $difficulty, $name, $maxClients, $eidCnt, $custom, $description, $motd, $port, $saveEnabled; - private $serverip, $evCnt, $handCnt, $events, $eventsID, $handlers, $serverType, $lastTick, $doTick, $ticks, $memoryStats, $schedule, $asyncThread, $async = array(), $asyncID = 0; + /** + * @var Server + */ + private static $instance = null; /** - * @var ServerAPI + * @var PluginManager */ - public $api; + private $pluginManager = null; - private function load(){ - $this->version = new VersionString(); - if(defined("PocketMine\\DEBUG") and \PocketMine\DEBUG >= 0 and function_exists("cli_set_process_title")){ - @cli_set_process_title("PocketMine-MP " . \PocketMine\VERSION); - } - console("[INFO] Starting Minecraft PE server on " . ($this->serverip === "0.0.0.0" ? "*" : $this->serverip) . ":" . $this->port); - define("BOOTUP_RANDOM", Utils::getRandomBytes(16)); - $this->serverID = $this->serverID === false ? Utils::readLong(substr(Utils::getUniqueID(true, $this->serverip . $this->port), 8)) : $this->serverID; - $this->seed = $this->seed === false ? Utils::readInt(Utils::getRandomBytes(4, false)) : $this->seed; - $this->startDatabase(); - $this->api = false; - $this->tCnt = 1; - $this->events = array(); - $this->eventsID = array(); - $this->handlers = array(); - $this->invisible = false; - $this->difficulty = 1; - $this->custom = array(); - $this->evCnt = 1; - $this->handCnt = 1; - $this->eidCnt = 1; - $this->maxClients = 20; - $this->schedule = array(); - $this->scheduleCnt = 1; - $this->description = ""; - $this->memoryStats = array(); - $this->spawn = false; - $this->saveEnabled = true; - $this->whitelist = false; - $this->tickMeasure = array_fill(0, 40, 0); - $this->setType("normal"); - $this->interface = new Handler("255.255.255.255", $this->port, $this->serverip); - $this->stop = false; - $this->ticks = 0; - if(!defined("NO_THREADS")){ - $this->asyncThread = new \AsyncMultipleQueue(); - } + /** + * @var ServerScheduler + */ + private $scheduler = null; + + /** + * @var TickScheduler + */ + private $tickScheduler = null; + + /** + * @var CommandReader + */ + private $console = null; + + /** + * @var SimpleCommandMap + */ + private $commandMap = null; + + /** + * @var ConsoleCommandSender + */ + private $consoleSender; + + /** + * @var int + */ + private $maxPlayers; + + /** + * Counts the ticks since the server start + * + * @var int + */ + private $tickCounter; + private $inTick = false; + + /** + * @var Handler + */ + private $interface; + + private $serverID; + + private $filePath; + private $dataPath; + private $pluginPath; + + /** + * @var Config + */ + private $properties; + + /** + * @return string + */ + public function getName(){ + return "PocketMine-MP"; } - function __construct($name, $gamemode = 0, $seed = false, $port = 19132, $serverip = "0.0.0.0"){ - $this->port = (int) $port; - $this->doTick = true; - $this->gamemode = (int) $gamemode; - $this->name = $name; - $this->motd = "Welcome to " . $name; - $this->serverID = false; - $this->seed = $seed; - $this->serverip = $serverip; - $this->load(); + /** + * @return string + */ + public function getPocketMineVersion(){ + return \PocketMine\VERSION; } + /** + * @return string + */ + public function getCodename(){ + return \PocketMine\CODENAME; + } + + /** + * @return string + */ + public function getVersion(){ + return \PocketMine\MINECRAFT_VERSION; + } + + /** + * @return string + */ + public function getApiVersion(){ + return \PocketMine\API_VERSION; + } + + /** + * @return string + */ + public function getFilePath(){ + return $this->filePath; + } + + /** + * @return string + */ + public function getDataPath(){ + return $this->dataPath; + } + + /** + * @return string + */ + public function getPluginPath(){ + return $this->pluginPath; + } + + /** + * @return int + */ + public function getMaxPlayers(){ + return $this->maxPlayers; + } + + /** + * @return int + */ + public function getPort(){ + return $this->getConfigInt("server-port", 19132); + } + + /** + * @return int + */ + public function getViewDistance(){ + return $this->getConfigInt("view-distance", 8); + } + + /** + * @return string + */ + public function getIp(){ + return $this->getConfigString("server-ip", ""); + } + + /** + * @return string + */ + public function getServerName(){ + return $this->getConfigString("server-name", "Unknown server"); + } + + /** + * @return string + */ + public function getLevelType(){ + return $this->getConfigString("level-type", "DEFAULT"); + } + + /** + * @return bool + */ + public function getGenerateStructures(){ + return $this->getConfigBoolean("generate-structures", true); + } + + /** + * @return int + */ + public function getGamemode(){ + return $this->getConfigInt("gamemode", 0) & 0b11; + } + + /** + * @return int + */ + public function getDifficulty(){ + return $this->getConfigInt("difficulty", 1); + } + + /** + * @return bool + */ + public function hasWhitelist(){ + return $this->getConfigBoolean("white-list", false); + } + + /** + * @return PluginManager + */ + public function getPluginManager(){ + return $this->pluginManager; + } + + /** + * @return ServerScheduler + */ + public function getScheduler(){ + return $this->scheduler; + } + + /** + * @return int + */ + public function getTick(){ + return $this->tickCounter; + } /** * @return float */ - public function getTPS(){ + public function getTicksPerSecond(){ $v = array_values($this->tickMeasure); - $tps = 40 / ($v[39] - $v[0]); + $tps = 20 / ($v[19] - $v[0]); return round($tps, 4); } + /** + * @param string $variable + * @param string $defaultValue + * + * @return string + */ + public function getConfigString($variable, $defaultValue = ""){ + $v = getopt("", array("$variable::")); + if(isset($v[$variable])){ + return (string) $v[$variable]; + } + return $this->properties->exists($variable) ? $this->properties->get($variable) : $defaultValue; + } + + /** + * @param string $variable + * @param string $value + */ + public function setConfigString($variable, $value){ + $this->properties->set($variable, $value); + } + + /** + * @param string $variable + * @param int $defaultValue + * + * @return int + */ + public function getConfigInt($variable, $defaultValue = 0){ + $v = getopt("", array("$variable::")); + if(isset($v[$variable])){ + return (int) $v[$variable]; + } + return $this->properties->exists($variable) ? (int) $this->properties->get($variable) : (int) $defaultValue; + } + + /** + * @param string $variable + * @param int $value + */ + public function setConfigInt($variable, $value){ + $this->properties->set($variable, (int) $value); + } + + /** + * @param string $variable + * @param boolean $defaultValue + * + * @return boolean + */ + public function getConfigBoolean($variable, $defaultValue = false){ + $v = getopt("", array("$variable::")); + if(isset($v[$variable])){ + $value = $v[$variable]; + }else{ + $value = $this->properties->exists($variable) ? $this->properties->get($variable) : $defaultValue; + } + if(is_bool($value)){ + return $value; + } + switch(strtolower($value)){ + case "on": + case "true": + case "1": + case "yes": + return true; + } + return false; + } + + /** + * @param string $variable + * @param bool $value + */ + public function setConfigBool($variable, $value){ + $this->properties->set($variable, $value == true ? "1":"0"); + } + + /** + * @return Server + */ + public static function getInstance(){ + return self::$instance; + } + + /** + * @param string $filePath + * @param string $dataPath + * @param string $pluginPath + */ + public function __construct($filePath, $dataPath, $pluginPath){ + self::$instance = $this; + + $this->filePath = $filePath; + $this->dataPath = $dataPath; + $this->pluginPath = $pluginPath; + @mkdir($this->dataPath . "worlds/", 0777); + @mkdir($this->dataPath . "players/", 0777); + @mkdir($this->pluginPath . "plugins/", 0777); + + $this->tickScheduler = new TickScheduler(20); + $this->scheduler = new ServerScheduler(); + $this->console = new CommandReader(); + + $version = new VersionString($this->getPocketMineVersion()); + console("[INFO] Starting Minecraft: PE server version " . TextFormat::AQUA . $this->getVersion()); + + console("[INFO] Loading properties..."); + $this->properties = new Config($this->dataPath . "server.properties", Config::PROPERTIES, array( + "server-name" => "Minecraft: PE Server", + "motd" => "Welcome @player to this server!", + "server-port" => 19132, + "server-type" => "normal", + "memory-limit" => "128M", + "white-list" => false, + "announce-player-achievements" => true, + "spawn-protection" => 16, + "view-distance" => 8, + "max-players" => 20, + "allow-flight" => false, + "spawn-animals" => true, + "spawn-mobs" => true, + "gamemode" => 0, + "hardcore" => false, + "pvp" => true, + "difficulty" => 1, + "generator-settings" => "", + "level-name" => "world", + "level-seed" => "", + "level-type" => "DEFAULT", + "enable-query" => true, + "enable-rcon" => false, + "rcon.password" => substr(base64_encode(Utils::getRandomBytes(20, false)), 3, 10), + "auto-save" => true, + )); + + $this->maxPlayers = $this->getConfigInt("max-players", 20); + + if(($memory = str_replace("B", "", strtoupper($this->getConfigString("memory-limit", "128M")))) !== false){ + $value = array("M" => 1, "G" => 1024); + $real = ((int) substr($memory, 0, -1)) * $value[substr($memory, -1)]; + if($real < 128){ + console("[WARNING] PocketMine-MP may not work right with less than 128MB of RAM", true, true, 0); + } + @ini_set("memory_limit", $memory); + }else{ + $this->setConfigString("memory-limit", "128M"); + } + + if($this->getConfigBoolean("hardcore", false) === true and $this->getDifficulty() < 3){ + $this->setConfigInt("difficulty", 3); + } + + define("PocketMine\\DEBUG", $this->getConfigInt("debug", 1)); + define("ADVANCED_CACHE", $this->getConfigBoolean("enable-advanced-cache", false)); + define("MAX_CHUNK_RATE", 20 / $this->getConfigInt("max-chunks-per-second", 7)); //Default rate ~448 kB/s + if(ADVANCED_CACHE == true){ + console("[INFO] Advanced cache enabled"); + } + if($this->getConfigBoolean("upnp-forwarding", false) == true){ + console("[INFO] [UPnP] Trying to port forward..."); + UPnP::PortForward($this->getPort()); + } + + if(defined("PocketMine\\DEBUG") and \PocketMine\DEBUG >= 0 and function_exists("cli_set_process_title")){ + @cli_set_process_title("PocketMine-MP " . $this->getPocketMineVersion()); + } + + console("[INFO] Starting Minecraft PE server on " . ($this->getIp() === "" ? "*" : $this->getIp()) . ":" . $this->getPort()); + define("BOOTUP_RANDOM", Utils::getRandomBytes(16)); + $this->serverID = Utils::readLong(substr(Utils::getUniqueID(true, $this->getIp() . $this->getPort()), 8)); + $this->interface = new Handler("255.255.255.255", $this->getPort(), $this->getIp() === "" ? "0.0.0.0":$this->getIp()); + + console("[INFO] This server is running PocketMine-MP version " . ($version->isDev() ? TextFormat::YELLOW : "") . $this->getPocketMineVersion() . TextFormat::RESET . " \"" . $this->getCodename() . "\" (API " . $this->getApiVersion() . ")", true, true, 0); + console("[INFO] PocketMine-MP is distributed under the LGPL License", true, true, 0); + + //TODO: update checking (async) + + $this->commandMap = new SimpleCommandMap($this); + + Block::init(); + Item::init(); + Crafting::init(); + Level::init(); + + $this->pluginManager = new PluginManager(); + console("[INFO] Loaded " . count($this->pluginManager->loadPlugins($this->pluginPath . "plugins/")) . " plugin(s)."); + + $this->consoleSender = new ConsoleCommandSender(); + + $this->properties->save(); + //TODO + /*if($this->getProperty("send-usage", true) !== false){ + $this->server->schedule(6000, array($this, "sendUsage"), array(), true); //Send the info after 5 minutes have passed + $this->sendUsage(); + } + if($this->getProperty("auto-save") === true){ + $this->server->schedule(18000, array($this, "autoSave"), array(), true); + } + if(!defined("NO_THREADS") and $this->getProperty("enable-rcon") === true){ + $this->rcon = new RCON($this->getProperty("rcon.password", ""), $this->getProperty("rcon.port", $this->getProperty("server-port")), ($ip = $this->getProperty("server-ip")) != "" ? $ip : "0.0.0.0", $this->getProperty("rcon.threads", 1), $this->getProperty("rcon.clients-per-thread", 50)); + } + + if($this->getProperty("enable-query") === true){ + $this->query = new QueryHandler(); + }*/ + + //$this->schedule(2, array($this, "checkTickUpdates"), array(), true); + + } + + public function checkConsole(){ + if(($line = $this->console->getLine()) !== null){ + $this->commandMap->dispatch($this->consoleSender, $line); + } + } + + /** + * Starts the PocketMine-MP server and starts processing ticks and packets + */ + public function start(){ + $this->tickCounter = 0; + register_tick_function(array($this, "tick")); + /* + register_shutdown_function(array($this, "dumpError")); + register_shutdown_function(array($this, "close")); + if(function_exists("pcntl_signal")){ + //pcntl_signal(SIGTERM, array($this, "close")); + pcntl_signal(SIGINT, array($this, "close")); + pcntl_signal(SIGHUP, array($this, "close")); + } + */ + console("[INFO] Default game type: " . strtoupper($this->getGamemode())); //TODO: string name + //$this->trigger("server.start", microtime(true)); + console('[INFO] Done (' . round(microtime(true) - \PocketMine\START_TIME, 3) . 's)! For help, type "help" or "?"'); + if(Utils::getOS() === "win"){ //Workaround less usleep() waste + $this->tickProcessorWindows(); + }else{ + $this->tickProcessor(); + } + } + + private function tickProcessorWindows(){ $lastLoop = 0; + + while(true){ + if(($packet = $this->interface->readPacket()) instanceof Packet){ + var_dump($packet); + $lastLoop = 0; + } + if(($ticks = $this->tick()) !== true){ + ++$lastLoop; + if($lastLoop > 128){ + usleep(1000); + } + }else{ + $lastLoop = 0; + } + } + } + + private function tickProcessor(){ + $lastLoop = 0; + while(true){ + if(($packet = $this->interface->readPacket()) instanceof Packet){ + var_dump($packet); + $lastLoop = 0; + } + if(($ticks = $this->tick()) !== true){ + ++$lastLoop; + if($lastLoop > 16 and $lastLoop < 128){ + usleep(200); + }elseif($lastLoop < 512){ + usleep(400); + }else{ + usleep(1000); + } + }else{ + $lastLoop = 0; + } + } + } + + private function checkTickUpdates(){ + //Update entities that need update + if(count(Entity::$needUpdate) > 0){ + foreach(Entity::$needUpdate as $id => $entity){ + if($entity->onUpdate() === false){ + unset(Entity::$needUpdate[$id]); + } + } + } + + //Update tiles that need update + if(count(Tile::$needUpdate) > 0){ + foreach(Tile::$needUpdate as $id => $tile){ + if($tile->onUpdate() === false){ + unset(Tile::$needUpdate[$id]); + } + } + } + + //TODO: Add level blocks + + //Do level ticks + foreach(Level::$list as $level){ + $level->doTick(); + } + } + + public function autoSave(){ + console("[DEBUG] Saving....", true, true, 2); + Level::saveAll(); + } + + public function sendUsage(){ + //TODO + /*console("[DEBUG] Sending usage data...", true, true, 2); + $plist = ""; + foreach(Server::getInstance()->getPluginManager()->getPlugins() as $p){ + $d = $p->getDescription(); + $plist .= str_replace(array(";", ":"), "", $d->getName()) . ":" . str_replace(array(";", ":"), "", $d->getVersion()) . ";"; + } + + $this->asyncOperation(ASYNC_CURL_POST, array( + "url" => "http://stats.pocketmine.net/usage.php", + "data" => array( + "serverid" => $this->server->serverID, + "port" => $this->server->port, + "os" => Utils::getOS(), + "memory_total" => $this->getProperty("memory-limit"), + "memory_usage" => memory_get_usage(true), + "php_version" => PHP_VERSION, + "version" => VERSION, + "mc_version" => MINECRAFT_VERSION, + "protocol" => Info::CURRENT_PROTOCOL, + "online" => count(Player::$list), + "max" => $this->server->maxClients, + "plugins" => $plist, + ), + ), null);*/ + } + public function titleTick(){ $time = microtime(true); if(defined("PocketMine\\DEBUG") and \PocketMine\DEBUG >= 0 and \PocketMine\ANSI === true){ - echo "\x1b]0;PocketMine-MP " . VERSION . " | Online " . count(Player::$list) . "/" . $this->maxClients . " | RAM " . round((memory_get_usage() / 1024) / 1024, 2) . "MB | U " . round(($this->interface->bandwidth[1] / max(1, $time - $this->interface->bandwidth[2])) / 1024, 2) . " D " . round(($this->interface->bandwidth[0] / max(1, $time - $this->interface->bandwidth[2])) / 1024, 2) . " kB/s | TPS " . $this->getTPS() . "\x07"; + echo "\x1b]0;PocketMine-MP " .$this->getPocketMineVersion() . " | Online " . count(Player::$list) . "/" . $this->getMaxPlayers() . " | RAM " . round((memory_get_usage() / 1024) / 1024, 2) . "MB | U " . round(($this->interface->bandwidth[1] / max(1, $time - $this->interface->bandwidth[2])) / 1024, 2) . " D " . round(($this->interface->bandwidth[0] / max(1, $time - $this->interface->bandwidth[2])) / 1024, 2) . " kB/s | TPS " . $this->getTPS() . "\x07"; } $this->interface->bandwidth = array(0, 0, $time); } - public function loadEvents(){ - if(\PocketMine\ANSI === true){ - $this->schedule(30, array($this, "titleTick"), array(), true); - } - $this->schedule(20 * 15, array($this, "checkTicks"), array(), true); - $this->schedule(20 * 60, array($this, "checkMemory"), array(), true); - $this->schedule(20 * 45, "PocketMine\\Utils\\Cache::cleanup", array(), true); - $this->schedule(20, array($this, "asyncOperationChecker"), array(), true); - } - - public function checkTicks(){ - if($this->getTPS() < 12){ - console("[WARNING] Can't keep up! Is the server overloaded?"); - } - } - - public function checkMemory(){ - $info = $this->debugInfo(); - $data = $info["memory_usage"] . "," . $info["players"] . "," . $info["entities"]; - $i = count($this->memoryStats) - 1; - if($i < 0 or $this->memoryStats[$i] !== $data){ - $this->memoryStats[] = $data; - } - } - - public function startDatabase(){ - $this->preparedSQL = new \stdClass(); - $this->preparedSQL->entity = new \stdClass(); - $this->database = new \SQLite3(":memory:", SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE); - $this->query("PRAGMA journal_mode = OFF;"); - $this->query("PRAGMA encoding = \"UTF-8\";"); - $this->query("PRAGMA secure_delete = OFF;"); - $this->query("CREATE TABLE actions (ID INTEGER PRIMARY KEY, interval NUMERIC, last NUMERIC, code TEXT, repeat NUMERIC);"); - $this->query("CREATE TABLE handlers (ID INTEGER PRIMARY KEY, name TEXT, priority NUMERIC);"); - $this->query("CREATE TABLE blockUpdates (level TEXT, x INTEGER, y INTEGER, z INTEGER, type INTEGER, delay NUMERIC);"); - $this->query("CREATE TABLE recipes (id INTEGER PRIMARY KEY, type NUMERIC, recipe TEXT);"); - $this->query("PRAGMA synchronous = OFF;"); - $this->preparedSQL->selectHandlers = $this->database->prepare("SELECT DISTINCT ID FROM handlers WHERE name = :name ORDER BY priority DESC;"); - $this->preparedSQL->selectActions = $this->database->prepare("SELECT ID,code,repeat FROM actions WHERE last <= (:time - interval);"); - $this->preparedSQL->updateAction = $this->database->prepare("UPDATE actions SET last = :time WHERE ID = :id;"); - } - - public function query($sql, $fetch = false){ - $result = $this->database->query($sql) or console("[ERROR] [SQL Error] " . $this->database->lastErrorMsg() . ". Query: " . $sql, true, true, 0); - if($fetch === true and ($result instanceof \SQLite3Result)){ - $result = $result->fetchArray(SQLITE3_ASSOC); - } - - return $result; - } - - public function debugInfo($console = false){ - $info = array(); - $info["tps"] = $this->getTPS(); - $info["memory_usage"] = round((memory_get_usage() / 1024) / 1024, 2) . "MB"; - $info["memory_peak_usage"] = round((memory_get_peak_usage() / 1024) / 1024, 2) . "MB"; - $info["entities"] = count(Entity::$list); - $info["players"] = count(Player::$list); - $info["events"] = count($this->eventsID); - $info["handlers"] = $this->query("SELECT count(ID) as count FROM handlers;", true); - $info["handlers"] = $info["handlers"]["count"]; - $info["actions"] = $this->query("SELECT count(ID) as count FROM actions;", true); - $info["actions"] = $info["actions"]["count"]; - $info["garbage"] = gc_collect_cycles(); - $this->handle("server.debug", $info); - if($console === true){ - console("[DEBUG] TPS: " . $info["tps"] . ", Memory usage: " . $info["memory_usage"] . " (Peak " . $info["memory_peak_usage"] . "), Entities: " . $info["entities"] . ", Events: " . $info["events"] . ", Handlers: " . $info["handlers"] . ", Actions: " . $info["actions"] . ", Garbage: " . $info["garbage"], true, true, 2); - } - - return $info; - } - /** - * @param string $reason - */ - public function close($reason = "server stop"){ - if($this->stop !== true){ - if(is_int($reason)){ - $reason = "signal stop"; - } - if(($this->api instanceof ServerAPI) === true){ - if(($this->api->chat instanceof ChatAPI) === true){ - Player::broadcastChat("Stopping server..."); - } - } - $this->stop = true; - $this->trigger("server.close", $reason); - $this->interface->close(); - - if(!defined("NO_THREADS")){ - @$this->asyncThread->stop = true; - } - } - } - - public function setType($type = "normal"){ - switch(trim(strtolower($type))){ - case "normal": - case "demo": - $this->serverType = "MCCPP;Demo;"; - break; - case "minecon": - $this->serverType = "MCCPP;MINECON;"; - break; - } - - } - - public function asyncOperation($type, array $data, callable $callable = null){ - if(defined("NO_THREADS")){ - return false; - } - $d = ""; - $type = (int) $type; - switch($type){ - case ASYNC_CURL_GET: - $d .= Utils::writeShort(strlen($data["url"])) . $data["url"] . (isset($data["timeout"]) ? Utils::writeShort($data["timeout"]) : Utils::writeShort(10)); - break; - case ASYNC_CURL_POST: - $d .= Utils::writeShort(strlen($data["url"])) . $data["url"] . (isset($data["timeout"]) ? Utils::writeShort($data["timeout"]) : Utils::writeShort(10)); - $d .= Utils::writeShort(count($data["data"])); - foreach($data["data"] as $key => $value){ - $d .= Utils::writeShort(strlen($key)) . $key . Utils::writeInt(strlen($value)) . $value; - } - break; - case ASYNC_FUNCTION: - $params = serialize($data["arguments"]); - $d .= Utils::writeShort(strlen($data["function"])) . $data["function"] . Utils::writeInt(strlen($params)) . $params; - break; - default: - return false; - } - $ID = $this->asyncID++; - $this->async[$ID] = $callable; - $this->asyncThread->input .= Utils::writeInt($ID) . Utils::writeShort($type) . $d; - - return $ID; - } - - public function asyncOperationChecker(){ - if(defined("NO_THREADS")){ - return false; - } - if(isset($this->asyncThread->output{5})){ - $offset = 0; - $ID = Utils::readInt(substr($this->asyncThread->output, $offset, 4)); - $offset += 4; - $type = Utils::readShort(substr($this->asyncThread->output, $offset, 2)); - $offset += 2; - $data = array(); - switch($type){ - case ASYNC_CURL_GET: - case ASYNC_CURL_POST: - $len = Utils::readInt(substr($this->asyncThread->output, $offset, 4)); - $offset += 4; - $data["result"] = substr($this->asyncThread->output, $offset, $len); - $offset += $len; - break; - case ASYNC_FUNCTION: - $len = Utils::readInt(substr($this->asyncThread->output, $offset, 4)); - $offset += 4; - $data["result"] = unserialize(substr($this->asyncThread->output, $offset, $len)); - $offset += $len; - break; - } - $this->asyncThread->output = substr($this->asyncThread->output, $offset); - if(isset($this->async[$ID]) and $this->async[$ID] !== null and is_callable($this->async[$ID])){ - if(is_array($this->async[$ID])){ - $method = $this->async[$ID][1]; - $result = $this->async[$ID][0]->$method($data, $type, $ID); - }else{ - $result = $this->async[$ID]($data, $type, $ID); - } - } - unset($this->async[$ID]); - } - } - - /** - * @param string $event - * @param callable $callable - * @param integer $priority + * Returns the last server TPS measure * - * @return boolean + * @return float */ - public function addHandler($event, callable $callable, $priority = 5){ - if(!is_callable($callable)){ - return false; - } - $priority = (int) $priority; - $hnid = $this->handCnt++; - $this->handlers[$hnid] = $callable; - $this->query("INSERT INTO handlers (ID, name, priority) VALUES (" . $hnid . ", '" . str_replace("'", "\\'", $event) . "', " . $priority . ");"); - console("[INTERNAL] New handler " . (is_array($callable) ? get_class($callable[0]) . "::" . $callable[1] : $callable) . " to special event " . $event . " (ID " . $hnid . ")", true, true, 3); - - return $hnid; - } - - public function dhandle($e, $d){ - return $this->handle($e, $d); - } - - public function handle($event, &$data){ - $this->preparedSQL->selectHandlers->reset(); - $this->preparedSQL->selectHandlers->clear(); - $this->preparedSQL->selectHandlers->bindValue(":name", $event, SQLITE3_TEXT); - $handlers = $this->preparedSQL->selectHandlers->execute(); - $result = null; - if($handlers instanceof \SQLite3Result){ - $call = array(); - while(($hn = $handlers->fetchArray(SQLITE3_ASSOC)) !== false){ - $call[(int) $hn["ID"]] = true; - } - $handlers->finalize(); - foreach($call as $hnid => $boolean){ - if($result !== false and $result !== true){ - $handler = $this->handlers[$hnid]; - if(is_array($handler)){ - $method = $handler[1]; - $result = $handler[0]->$method($data, $event); - }else{ - $result = $handler($data, $event); - } - }else{ - break; - } - } - } - - if($result !== false){ - $this->trigger($event, $data); - } - - return $result; - } - - public function eventHandler($data, $event){ - switch($event){ - - } + public function getTPS(){ + return $this->tickScheduler->getTPS(); } /** - * TODO - * @return string + * Tries to execute a server tick */ - public function getGamemode(){ - switch($this->gamemode){ - case 0: - return "survival"; - case 1: - return "creative"; - case 2: - return "adventure"; - case 3: - return "view"; - } - } - - - public function init(){ - register_tick_function(array($this, "tick")); - declare(ticks = 5000); //Minimum TPS for main thread locks - - $this->loadEvents(); - register_shutdown_function(array($this, "dumpError")); - register_shutdown_function(array($this, "close")); - if(function_exists("pcntl_signal")){ - pcntl_signal(SIGTERM, array($this, "close")); - pcntl_signal(SIGINT, array($this, "close")); - pcntl_signal(SIGHUP, array($this, "close")); - } - console("[INFO] Default game type: " . strtoupper($this->getGamemode())); - $this->trigger("server.start", microtime(true)); - console('[INFO] Done (' . round(microtime(true) - \PocketMine\START_TIME, 3) . 's)! For help, type "help" or "?"'); - $this->process(); - } - - public function dumpError(){ - if($this->stop === true){ - return; - } - ini_set("memory_limit", "-1"); //Fix error dump not dumped on memory problems - console("[SEVERE] An unrecovereable has ocurred and the server has crashed. Creating an error dump"); - $dump = "```\r\n# PocketMine-MP Error Dump " . date("D M j H:i:s T Y") . "\r\n"; - $er = error_get_last(); - $errorConversion = array( - E_ERROR => "E_ERROR", - E_WARNING => "E_WARNING", - E_PARSE => "E_PARSE", - E_NOTICE => "E_NOTICE", - E_CORE_ERROR => "E_CORE_ERROR", - E_CORE_WARNING => "E_CORE_WARNING", - E_COMPILE_ERROR => "E_COMPILE_ERROR", - E_COMPILE_WARNING => "E_COMPILE_WARNING", - E_USER_ERROR => "E_USER_ERROR", - E_USER_WARNING => "E_USER_WARNING", - E_USER_NOTICE => "E_USER_NOTICE", - E_STRICT => "E_STRICT", - E_RECOVERABLE_ERROR => "E_RECOVERABLE_ERROR", - E_DEPRECATED => "E_DEPRECATED", - E_USER_DEPRECATED => "E_USER_DEPRECATED", - ); - $er["type"] = isset($errorConversion[$er["type"]]) ? $errorConversion[$er["type"]] : $er["type"]; - $dump .= "Error: " . var_export($er, true) . "\r\n\r\n"; - if(stripos($er["file"], "plugin") !== false){ - $dump .= "THIS ERROR WAS CAUSED BY A PLUGIN. REPORT IT TO THE PLUGIN DEVELOPER.\r\n"; - } - - $dump .= "Code: \r\n"; - $file = @file($er["file"], FILE_IGNORE_NEW_LINES); - for($l = max(0, $er["line"] - 10); $l < $er["line"] + 10; ++$l){ - $dump .= "[" . ($l + 1) . "] " . @$file[$l] . "\r\n"; - } - $dump .= "\r\n\r\n"; - $dump .= "Backtrace: \r\n"; - foreach(getTrace() as $line){ - $dump .= "$line\r\n"; - } - $dump .= "\r\n\r\n"; - $version = new VersionString(); - $dump .= "PocketMine-MP version: " . $version . " #" . $version->getNumber() . " [Protocol " . Info::CURRENT_PROTOCOL . "; API " . API_VERSION . "]\r\n"; - $dump .= "Git commit: " . GIT_COMMIT . "\r\n"; - $dump .= "uname -a: " . php_uname("a") . "\r\n"; - $dump .= "PHP Version: " . phpversion() . "\r\n"; - $dump .= "Zend version: " . zend_version() . "\r\n"; - $dump .= "OS : " . PHP_OS . ", " . Utils::getOS() . "\r\n"; - $dump .= "Debug Info: " . var_export($this->debugInfo(false), true) . "\r\n\r\n\r\n"; - global $arguments; - $dump .= "Parameters: " . var_export($arguments, true) . "\r\n\r\n\r\n"; - $p = $this->api->getProperties(); - if($p["rcon.password"] != ""){ - $p["rcon.password"] = "******"; - } - $dump .= "server.properties: " . var_export($p, true) . "\r\n\r\n\r\n"; - if(class_exists("PocketMine\\Plugin\\PluginManager", false)){ - $dump .= "Loaded plugins:\r\n"; - foreach(PluginManager::getPlugins() as $p){ - $d = $p->getDescription(); - $dump .= $d->getName() . " " . $d->getVersion() . " by " . implode(", ", $d->getAuthors()) . "\r\n"; - } - $dump .= "\r\n\r\n"; - } - - $extensions = array(); - foreach(get_loaded_extensions() as $ext){ - $extensions[$ext] = phpversion($ext); - } - - $dump .= "Loaded Modules: " . var_export($extensions, true) . "\r\n"; - $this->checkMemory(); - $dump .= "Memory Usage Tracking: \r\n" . chunk_split(base64_encode(gzdeflate(implode(";", $this->memoryStats), 9))) . "\r\n"; - ob_start(); - phpinfo(); - $dump .= "\r\nphpinfo(): \r\n" . chunk_split(base64_encode(gzdeflate(ob_get_contents(), 9))) . "\r\n"; - ob_end_clean(); - $dump .= "\r\n```"; - $name = "Error_Dump_" . date("D_M_j-H.i.s-T_Y"); - log($dump, $name, true, 0, true); - console("[SEVERE] Please submit the \"{$name}.log\" file to the Bug Reporting page. Give as much info as you can.", true, true, 0); - } - public function tick(){ - $time = microtime(true); - if($this->lastTick <= ($time - 0.05)){ - $this->tickMeasure[] = $this->lastTick = $time; - unset($this->tickMeasure[key($this->tickMeasure)]); - ++$this->ticks; + if($this->inTick === false and $this->tickScheduler->hasTick()){ + $this->inTick = true; //Fix race conditions + ++$this->tickCounter; - return $this->tickerFunction($time); - } - - return 0; - } - - public static function clientID($ip, $port){ - return crc32($ip . $port) ^ crc32($port . $ip . BOOTUP_RANDOM); - //return $ip . ":" . $port; - } - - public function packetHandler(Packet $packet){ - $data =& $packet; - $CID = Server::clientID($packet->ip, $packet->port); - if(isset(Player::$list[$CID])){ - Player::$list[$CID]->handlePacket($packet); - }else{ - switch($packet->pid()){ - case RakNetInfo::UNCONNECTED_PING: - case RakNetInfo::UNCONNECTED_PING_OPEN_CONNECTIONS: - if($this->invisible === true){ - $pk = new RakNetPacket(RakNetInfo::UNCONNECTED_PONG); - $pk->pingID = $packet->pingID; - $pk->serverID = $this->serverID; - $pk->serverType = $this->serverType; - $pk->ip = $packet->ip; - $pk->port = $packet->port; - $this->send($pk); - break; - } - if(!isset($this->custom["times_" . $CID])){ - $this->custom["times_" . $CID] = 0; - } - $ln = 15; - if($this->description == "" or substr($this->description, -1) != " "){ - $this->description .= " "; - } - $txt = substr($this->description, $this->custom["times_" . $CID], $ln); - $txt .= substr($this->description, 0, $ln - strlen($txt)); - $pk = new RakNetPacket(RakNetInfo::UNCONNECTED_PONG); - $pk->pingID = $packet->pingID; - $pk->serverID = $this->serverID; - $pk->serverType = $this->serverType . $this->name . " [" . count(Player::$list) . "/" . $this->maxClients . "] " . $txt; - $pk->ip = $packet->ip; - $pk->port = $packet->port; - $this->send($pk); - $this->custom["times_" . $CID] = ($this->custom["times_" . $CID] + 1) % strlen($this->description); - break; - case RakNetInfo::OPEN_CONNECTION_REQUEST_1: - if($packet->structure !== RakNetInfo::STRUCTURE){ - console("[DEBUG] Incorrect structure #" . $packet->structure . " from " . $packet->ip . ":" . $packet->port, true, true, 2); - $pk = new RakNetPacket(RakNetInfo::INCOMPATIBLE_PROTOCOL_VERSION); - $pk->serverID = $this->serverID; - $pk->ip = $packet->ip; - $pk->port = $packet->port; - $this->send($pk); - }else{ - $pk = new RakNetPacket(RakNetInfo::OPEN_CONNECTION_REPLY_1); - $pk->serverID = $this->serverID; - $pk->mtuSize = strlen($packet->buffer); - $pk->ip = $packet->ip; - $pk->port = $packet->port; - $this->send($pk); - } - break; - case RakNetInfo::OPEN_CONNECTION_REQUEST_2: - if($this->invisible === true){ - break; - } - - new Player($packet->clientID, $packet->ip, $packet->port, $packet->mtuSize); //New Session! - $pk = new RakNetPacket(RakNetInfo::OPEN_CONNECTION_REPLY_2); - $pk->serverID = $this->serverID; - $pk->serverPort = $this->port; - $pk->mtuSize = $packet->mtuSize; - $pk->ip = $packet->ip; - $pk->port = $packet->port; - $this->send($pk); - break; - } - } - } - - public function send(Packet $packet){ - return $this->interface->writePacket($packet); - } - - public function process(){ - $lastLoop = 0; - while($this->stop === false){ - $packet = $this->interface->readPacket(); - if($packet instanceof Packet){ - $this->packetHandler($packet); - $lastLoop = 0; - } - if(($ticks = $this->tick()) === 0){ - ++$lastLoop; - if($lastLoop < 16){ - usleep(1); - }elseif($lastLoop < 128){ - usleep(1000); - }elseif($lastLoop < 256){ - usleep(2000); - }else{ - usleep(4000); + $this->checkConsole(); + $this->scheduler->mainThreadHeartbeat($this->tickCounter); + if(($this->tickCounter & 0b1) === 0){ + $this->checkTickUpdates(); + if(($this->tickCounter & 0b1111) === 0){ + $this->titleTick(); } } + $this->tickScheduler->doTick(); + $this->inTick = false; + return true; } + return false; } - public function trigger($event, $data = ""){ - if(isset($this->events[$event])){ - foreach($this->events[$event] as $evid => $ev){ - if(!is_callable($ev)){ - $this->deleteEvent($evid); - continue; - } - if(is_array($ev)){ - $method = $ev[1]; - $ev[0]->$method($data, $event); - }else{ - $ev($data, $event); - } - } - } - } - - public function schedule($ticks, callable $callback, $data = array(), $repeat = false, $eventName = "server.schedule"){ - if(!is_callable($callback)){ - return false; - } - $chcnt = $this->scheduleCnt++; - $this->schedule[$chcnt] = array($callback, $data, $eventName); - $this->query("INSERT INTO actions (ID, interval, last, repeat) VALUES(" . $chcnt . ", " . ($ticks / 20) . ", " . microtime(true) . ", " . (((bool) $repeat) === true ? 1 : 0) . ");"); - - return $chcnt; - } - - public function tickerFunction($time){ - //actions that repeat every x time will go here - $this->preparedSQL->selectActions->reset(); - $this->preparedSQL->selectActions->bindValue(":time", $time, SQLITE3_FLOAT); - $actions = $this->preparedSQL->selectActions->execute(); - - $actionCount = 0; - if($actions instanceof \SQLite3Result){ - while(($action = $actions->fetchArray(SQLITE3_ASSOC)) !== false){ - $cid = $action["ID"]; - $this->preparedSQL->updateAction->reset(); - $this->preparedSQL->updateAction->bindValue(":time", $time, SQLITE3_FLOAT); - $this->preparedSQL->updateAction->bindValue(":id", $cid, SQLITE3_INTEGER); - $this->preparedSQL->updateAction->execute(); - if(!@is_callable($this->schedule[$cid][0])){ - $return = false; - }else{ - ++$actionCount; - $return = call_user_func($this->schedule[$cid][0], $this->schedule[$cid][1], $this->schedule[$cid][2]); - } - - if($action["repeat"] == 0 or $return === false){ - $this->query("DELETE FROM actions WHERE ID = " . $action["ID"] . ";"); - $this->schedule[$cid] = null; - unset($this->schedule[$cid]); - } - } - $actions->finalize(); - } - - return $actionCount; - } - - public function event($event, callable $func){ - if(!is_callable($func)){ - return false; - } - $evid = $this->evCnt++; - if(!isset($this->events[$event])){ - $this->events[$event] = array(); - } - $this->events[$event][$evid] = $func; - $this->eventsID[$evid] = $event; - console("[INTERNAL] Attached " . (is_array($func) ? get_class($func[0]) . "::" . $func[1] : $func) . " to event " . $event . " (ID " . $evid . ")", true, true, 3); - - return $evid; - } - - public function deleteEvent($id){ - $id = (int) $id; - if(isset($this->eventsID[$id])){ - $ev = $this->eventsID[$id]; - $this->eventsID[$id] = null; - unset($this->eventsID[$id]); - $this->events[$ev][$id] = null; - unset($this->events[$ev][$id]); - if(count($this->events[$ev]) === 0){ - unset($this->events[$ev]); - } - } - } - -} +} \ No newline at end of file diff --git a/src/PocketMine/ServerAPI.php b/src/PocketMine/ServerAPI.php index 9bcd3ea25..710e65c4e 100644 --- a/src/PocketMine/ServerAPI.php +++ b/src/PocketMine/ServerAPI.php @@ -225,27 +225,6 @@ class ServerAPI{ } - public function checkTickUpdates(){ - //Update entities that need update - if(count(Entity::$needUpdate) > 0){ - foreach(Entity::$needUpdate as $id => $entity){ - if($entity->onUpdate() === false){ - unset(Entity::$needUpdate[$id]); - } - } - } - - //Update tiles that need update - if(count(Tile::$needUpdate) > 0){ - foreach(Tile::$needUpdate as $id => $tile){ - if($tile->onUpdate() === false){ - unset(Tile::$needUpdate[$id]); - } - } - } - - } - public function async(callable $callable, $params = array(), $remove = false){ $cnt = $this->asyncCnt++; $this->asyncCalls[$cnt] = new \Async($callable, $params); @@ -263,38 +242,6 @@ class ServerAPI{ return $ob; } - public function autoSave(){ - console("[DEBUG] Saving....", true, true, 2); - Level::saveAll(); - } - - public function sendUsage(){ - console("[DEBUG] Sending usage data...", true, true, 2); - $plist = ""; - foreach(PluginManager::getPlugins() as $p){ - $d = $p->getDescription(); - $plist .= str_replace(array(";", ":"), "", $d->getName()) . ":" . str_replace(array(";", ":"), "", $d->getVersion()) . ";"; - } - - $this->asyncOperation(ASYNC_CURL_POST, array( - "url" => "http://stats.pocketmine.net/usage.php", - "data" => array( - "serverid" => $this->server->serverID, - "port" => $this->server->port, - "os" => Utils::getOS(), - "memory_total" => $this->getProperty("memory-limit"), - "memory_usage" => memory_get_usage(true), - "php_version" => PHP_VERSION, - "version" => VERSION, - "mc_version" => MINECRAFT_VERSION, - "protocol" => Info::CURRENT_PROTOCOL, - "online" => count(Player::$list), - "max" => $this->server->maxClients, - "plugins" => $plist, - ), - ), null); - } - public function __destruct(){ foreach($this->apiList as $i => $ob){ if(method_exists($ob, "__destruct")){ @@ -304,73 +251,15 @@ class ServerAPI{ } } - - private function loadProperties(){ - if(($memory = str_replace("B", "", strtoupper($this->getProperty("memory-limit")))) !== false){ - $value = array("M" => 1, "G" => 1024); - $real = ((int) substr($memory, 0, -1)) * $value[substr($memory, -1)]; - if($real < 128){ - console("[WARNING] PocketMine-MP may not work right with less than 128MB of RAM", true, true, 0); - } - @ini_set("memory_limit", $memory); - }else{ - $this->setProperty("memory-limit", "128M"); - } - - if($this->server instanceof Server){ - $this->server->setType($this->getProperty("server-type")); - $this->server->maxClients = $this->getProperty("max-players"); - $this->server->description = $this->getProperty("description"); - $this->server->motd = $this->getProperty("motd"); - $this->server->gamemode = $this->getProperty("gamemode"); - $this->server->difficulty = $this->getProperty("difficulty"); - $this->server->whitelist = $this->getProperty("white-list"); - } - } - private function writeProperties(){ $this->config->save(); } - private function parseProperties(){ - foreach($this->config->getAll() as $n => $v){ - switch($n){ - case "last-update": - if($v === false){ - $v = time(); - }else{ - $v = (int) $v; - } - break; - case "gamemode": - case "max-players": - case "server-port": - case "debug": - case "difficulty": - $v = (int) $v; - break; - case "server-id": - if($v !== false){ - $v = preg_match("/[^0-9\-]/", $v) > 0 ? Utils::readInt(substr(md5($v, true), 0, 4)) : $v; - } - break; - } - $this->config->set($n, $v); - } - if($this->getProperty("hardcore") == 1 and $this->getProperty("difficulty") < 3){ - $this->setProperty("difficulty", 3); - } - } - public function init(){ if(!(self::$serverRequest instanceof Server)){ self::$serverRequest = $this->server; } - Block::init(); - Item::init(); - Crafting::init(); - Level::init(); if($this->getProperty("send-usage", true) !== false){ diff --git a/src/PocketMine/ServerOld.php b/src/PocketMine/ServerOld.php new file mode 100644 index 000000000..9dc8017ef --- /dev/null +++ b/src/PocketMine/ServerOld.php @@ -0,0 +1,735 @@ +version = new VersionString(); + if(defined("PocketMine\\DEBUG") and \PocketMine\DEBUG >= 0 and function_exists("cli_set_process_title")){ + @cli_set_process_title("PocketMine-MP " . \PocketMine\VERSION); + } + console("[INFO] Starting Minecraft PE server on " . ($this->serverip === "0.0.0.0" ? "*" : $this->serverip) . ":" . $this->port); + define("BOOTUP_RANDOM", Utils::getRandomBytes(16)); + $this->serverID = $this->serverID === false ? Utils::readLong(substr(Utils::getUniqueID(true, $this->serverip . $this->port), 8)) : $this->serverID; + $this->seed = $this->seed === false ? Utils::readInt(Utils::getRandomBytes(4, false)) : $this->seed; + $this->startDatabase(); + $this->api = false; + $this->tCnt = 1; + $this->events = array(); + $this->eventsID = array(); + $this->handlers = array(); + $this->invisible = false; + $this->difficulty = 1; + $this->custom = array(); + $this->evCnt = 1; + $this->handCnt = 1; + $this->eidCnt = 1; + $this->maxClients = 20; + $this->schedule = array(); + $this->scheduleCnt = 1; + $this->description = ""; + $this->memoryStats = array(); + $this->spawn = false; + $this->saveEnabled = true; + $this->whitelist = false; + $this->tickMeasure = array_fill(0, 40, 0); + $this->setType("normal"); + $this->interface = new Handler("255.255.255.255", $this->port, $this->serverip); + $this->stop = false; + $this->ticks = 0; + if(!defined("NO_THREADS")){ + $this->asyncThread = new \AsyncMultipleQueue(); + } + } + + function __construct($name, $gamemode = 0, $seed = false, $port = 19132, $serverip = "0.0.0.0"){ + $this->port = (int) $port; + $this->doTick = true; + $this->gamemode = (int) $gamemode; + $this->name = $name; + $this->motd = "Welcome to " . $name; + $this->serverID = false; + $this->seed = $seed; + $this->serverip = $serverip; + self::$instance = $this; + $this->load(); + } + + /** + * @return float + */ + public function getTPS(){ + $v = array_values($this->tickMeasure); + $tps = 40 / ($v[39] - $v[0]); + + return round($tps, 4); + } + + public function titleTick(){ + $time = microtime(true); + if(defined("PocketMine\\DEBUG") and \PocketMine\DEBUG >= 0 and \PocketMine\ANSI === true){ + echo "\x1b]0;PocketMine-MP " . VERSION . " | Online " . count(Player::$list) . "/" . $this->maxClients . " | RAM " . round((memory_get_usage() / 1024) / 1024, 2) . "MB | U " . round(($this->interface->bandwidth[1] / max(1, $time - $this->interface->bandwidth[2])) / 1024, 2) . " D " . round(($this->interface->bandwidth[0] / max(1, $time - $this->interface->bandwidth[2])) / 1024, 2) . " kB/s | TPS " . $this->getTPS() . "\x07"; + } + $this->interface->bandwidth = array(0, 0, $time); + } + + public function loadEvents(){ + if(\PocketMine\ANSI === true){ + $this->schedule(30, array($this, "titleTick"), array(), true); + } + $this->schedule(20 * 15, array($this, "checkTicks"), array(), true); + $this->schedule(20 * 60, array($this, "checkMemory"), array(), true); + $this->schedule(20 * 45, "PocketMine\\Utils\\Cache::cleanup", array(), true); + $this->schedule(20, array($this, "asyncOperationChecker"), array(), true); + } + + public function checkTicks(){ + if($this->getTPS() < 12){ + console("[WARNING] Can't keep up! Is the server overloaded?"); + } + } + + public function checkMemory(){ + $info = $this->debugInfo(); + $data = $info["memory_usage"] . "," . $info["players"] . "," . $info["entities"]; + $i = count($this->memoryStats) - 1; + if($i < 0 or $this->memoryStats[$i] !== $data){ + $this->memoryStats[] = $data; + } + } + + public function startDatabase(){ + $this->preparedSQL = new \stdClass(); + $this->preparedSQL->entity = new \stdClass(); + $this->database = new \SQLite3(":memory:", SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE); + $this->query("PRAGMA journal_mode = OFF;"); + $this->query("PRAGMA encoding = \"UTF-8\";"); + $this->query("PRAGMA secure_delete = OFF;"); + $this->query("CREATE TABLE actions (ID INTEGER PRIMARY KEY, interval NUMERIC, last NUMERIC, code TEXT, repeat NUMERIC);"); + $this->query("CREATE TABLE handlers (ID INTEGER PRIMARY KEY, name TEXT, priority NUMERIC);"); + $this->query("CREATE TABLE blockUpdates (level TEXT, x INTEGER, y INTEGER, z INTEGER, type INTEGER, delay NUMERIC);"); + $this->query("CREATE TABLE recipes (id INTEGER PRIMARY KEY, type NUMERIC, recipe TEXT);"); + $this->query("PRAGMA synchronous = OFF;"); + $this->preparedSQL->selectHandlers = $this->database->prepare("SELECT DISTINCT ID FROM handlers WHERE name = :name ORDER BY priority DESC;"); + $this->preparedSQL->selectActions = $this->database->prepare("SELECT ID,code,repeat FROM actions WHERE last <= (:time - interval);"); + $this->preparedSQL->updateAction = $this->database->prepare("UPDATE actions SET last = :time WHERE ID = :id;"); + } + + public function query($sql, $fetch = false){ + $result = $this->database->query($sql) or console("[ERROR] [SQL Error] " . $this->database->lastErrorMsg() . ". Query: " . $sql, true, true, 0); + if($fetch === true and ($result instanceof \SQLite3Result)){ + $result = $result->fetchArray(SQLITE3_ASSOC); + } + + return $result; + } + + public function debugInfo($console = false){ + $info = array(); + $info["tps"] = $this->getTPS(); + $info["memory_usage"] = round((memory_get_usage() / 1024) / 1024, 2) . "MB"; + $info["memory_peak_usage"] = round((memory_get_peak_usage() / 1024) / 1024, 2) . "MB"; + $info["entities"] = count(Entity::$list); + $info["players"] = count(Player::$list); + $info["events"] = count($this->eventsID); + $info["handlers"] = $this->query("SELECT count(ID) as count FROM handlers;", true); + $info["handlers"] = $info["handlers"]["count"]; + $info["actions"] = $this->query("SELECT count(ID) as count FROM actions;", true); + $info["actions"] = $info["actions"]["count"]; + $info["garbage"] = gc_collect_cycles(); + $this->handle("server.debug", $info); + if($console === true){ + console("[DEBUG] TPS: " . $info["tps"] . ", Memory usage: " . $info["memory_usage"] . " (Peak " . $info["memory_peak_usage"] . "), Entities: " . $info["entities"] . ", Events: " . $info["events"] . ", Handlers: " . $info["handlers"] . ", Actions: " . $info["actions"] . ", Garbage: " . $info["garbage"], true, true, 2); + } + + return $info; + } + + /** + * @param string $reason + */ + public function close($reason = "server stop"){ + if($this->stop !== true){ + if(is_int($reason)){ + $reason = "signal stop"; + } + if(($this->api instanceof ServerAPI) === true){ + if(($this->api->chat instanceof ChatAPI) === true){ + Player::broadcastMessage("Stopping server..."); + } + } + $this->stop = true; + $this->trigger("server.close", $reason); + $this->interface->close(); + + if(!defined("NO_THREADS")){ + @$this->asyncThread->stop = true; + } + } + } + + public function setType($type = "normal"){ + switch(trim(strtolower($type))){ + case "normal": + case "demo": + $this->serverType = "MCCPP;Demo;"; + break; + case "minecon": + $this->serverType = "MCCPP;MINECON;"; + break; + } + + } + + public function asyncOperation($type, array $data, callable $callable = null){ + if(defined("NO_THREADS")){ + return false; + } + $d = ""; + $type = (int) $type; + switch($type){ + case ASYNC_CURL_GET: + $d .= Utils::writeShort(strlen($data["url"])) . $data["url"] . (isset($data["timeout"]) ? Utils::writeShort($data["timeout"]) : Utils::writeShort(10)); + break; + case ASYNC_CURL_POST: + $d .= Utils::writeShort(strlen($data["url"])) . $data["url"] . (isset($data["timeout"]) ? Utils::writeShort($data["timeout"]) : Utils::writeShort(10)); + $d .= Utils::writeShort(count($data["data"])); + foreach($data["data"] as $key => $value){ + $d .= Utils::writeShort(strlen($key)) . $key . Utils::writeInt(strlen($value)) . $value; + } + break; + case ASYNC_FUNCTION: + $params = serialize($data["arguments"]); + $d .= Utils::writeShort(strlen($data["function"])) . $data["function"] . Utils::writeInt(strlen($params)) . $params; + break; + default: + return false; + } + $ID = $this->asyncID++; + $this->async[$ID] = $callable; + $this->asyncThread->input .= Utils::writeInt($ID) . Utils::writeShort($type) . $d; + + return $ID; + } + + public function asyncOperationChecker(){ + if(defined("NO_THREADS")){ + return false; + } + if(isset($this->asyncThread->output{5})){ + $offset = 0; + $ID = Utils::readInt(substr($this->asyncThread->output, $offset, 4)); + $offset += 4; + $type = Utils::readShort(substr($this->asyncThread->output, $offset, 2)); + $offset += 2; + $data = array(); + switch($type){ + case ASYNC_CURL_GET: + case ASYNC_CURL_POST: + $len = Utils::readInt(substr($this->asyncThread->output, $offset, 4)); + $offset += 4; + $data["result"] = substr($this->asyncThread->output, $offset, $len); + $offset += $len; + break; + case ASYNC_FUNCTION: + $len = Utils::readInt(substr($this->asyncThread->output, $offset, 4)); + $offset += 4; + $data["result"] = unserialize(substr($this->asyncThread->output, $offset, $len)); + $offset += $len; + break; + } + $this->asyncThread->output = substr($this->asyncThread->output, $offset); + if(isset($this->async[$ID]) and $this->async[$ID] !== null and is_callable($this->async[$ID])){ + if(is_array($this->async[$ID])){ + $method = $this->async[$ID][1]; + $result = $this->async[$ID][0]->$method($data, $type, $ID); + }else{ + $result = $this->async[$ID]($data, $type, $ID); + } + } + unset($this->async[$ID]); + } + } + + /** + * @param string $event + * @param callable $callable + * @param integer $priority + * + * @return boolean + */ + public function addHandler($event, callable $callable, $priority = 5){ + if(!is_callable($callable)){ + return false; + } + $priority = (int) $priority; + $hnid = $this->handCnt++; + $this->handlers[$hnid] = $callable; + $this->query("INSERT INTO handlers (ID, name, priority) VALUES (" . $hnid . ", '" . str_replace("'", "\\'", $event) . "', " . $priority . ");"); + console("[INTERNAL] New handler " . (is_array($callable) ? get_class($callable[0]) . "::" . $callable[1] : $callable) . " to special event " . $event . " (ID " . $hnid . ")", true, true, 3); + + return $hnid; + } + + public function dhandle($e, $d){ + return $this->handle($e, $d); + } + + public function handle($event, &$data){ + $this->preparedSQL->selectHandlers->reset(); + $this->preparedSQL->selectHandlers->clear(); + $this->preparedSQL->selectHandlers->bindValue(":name", $event, SQLITE3_TEXT); + $handlers = $this->preparedSQL->selectHandlers->execute(); + $result = null; + if($handlers instanceof \SQLite3Result){ + $call = array(); + while(($hn = $handlers->fetchArray(SQLITE3_ASSOC)) !== false){ + $call[(int) $hn["ID"]] = true; + } + $handlers->finalize(); + foreach($call as $hnid => $boolean){ + if($result !== false and $result !== true){ + $handler = $this->handlers[$hnid]; + if(is_array($handler)){ + $method = $handler[1]; + $result = $handler[0]->$method($data, $event); + }else{ + $result = $handler($data, $event); + } + }else{ + break; + } + } + } + + if($result !== false){ + $this->trigger($event, $data); + } + + return $result; + } + + public function eventHandler($data, $event){ + switch($event){ + + } + } + + /** + * TODO + * @return string + */ + public function getGamemode(){ + switch($this->gamemode){ + case 0: + return "survival"; + case 1: + return "creative"; + case 2: + return "adventure"; + case 3: + return "view"; + } + } + + + public function init(){ + register_tick_function(array($this, "tick")); + declare(ticks = 5000); //Minimum TPS for main thread locks + + $this->loadEvents(); + register_shutdown_function(array($this, "dumpError")); + register_shutdown_function(array($this, "close")); + if(function_exists("pcntl_signal")){ + pcntl_signal(SIGTERM, array($this, "close")); + pcntl_signal(SIGINT, array($this, "close")); + pcntl_signal(SIGHUP, array($this, "close")); + } + console("[INFO] Default game type: " . strtoupper($this->getGamemode())); + $this->trigger("server.start", microtime(true)); + console('[INFO] Done (' . round(microtime(true) - \PocketMine\START_TIME, 3) . 's)! For help, type "help" or "?"'); + $this->process(); + } + + public function dumpError(){ + if($this->stop === true){ + return; + } + ini_set("memory_limit", "-1"); //Fix error dump not dumped on memory problems + console("[SEVERE] An unrecovereable has ocurred and the server has crashed. Creating an error dump"); + $dump = "```\r\n# PocketMine-MP Error Dump " . date("D M j H:i:s T Y") . "\r\n"; + $er = error_get_last(); + $errorConversion = array( + E_ERROR => "E_ERROR", + E_WARNING => "E_WARNING", + E_PARSE => "E_PARSE", + E_NOTICE => "E_NOTICE", + E_CORE_ERROR => "E_CORE_ERROR", + E_CORE_WARNING => "E_CORE_WARNING", + E_COMPILE_ERROR => "E_COMPILE_ERROR", + E_COMPILE_WARNING => "E_COMPILE_WARNING", + E_USER_ERROR => "E_USER_ERROR", + E_USER_WARNING => "E_USER_WARNING", + E_USER_NOTICE => "E_USER_NOTICE", + E_STRICT => "E_STRICT", + E_RECOVERABLE_ERROR => "E_RECOVERABLE_ERROR", + E_DEPRECATED => "E_DEPRECATED", + E_USER_DEPRECATED => "E_USER_DEPRECATED", + ); + $er["type"] = isset($errorConversion[$er["type"]]) ? $errorConversion[$er["type"]] : $er["type"]; + $dump .= "Error: " . var_export($er, true) . "\r\n\r\n"; + if(stripos($er["file"], "plugin") !== false){ + $dump .= "THIS ERROR WAS CAUSED BY A PLUGIN. REPORT IT TO THE PLUGIN DEVELOPER.\r\n"; + } + + $dump .= "Code: \r\n"; + $file = @file($er["file"], FILE_IGNORE_NEW_LINES); + for($l = max(0, $er["line"] - 10); $l < $er["line"] + 10; ++$l){ + $dump .= "[" . ($l + 1) . "] " . @$file[$l] . "\r\n"; + } + $dump .= "\r\n\r\n"; + $dump .= "Backtrace: \r\n"; + foreach(getTrace() as $line){ + $dump .= "$line\r\n"; + } + $dump .= "\r\n\r\n"; + $version = new VersionString(); + $dump .= "PocketMine-MP version: " . $version . " #" . $version->getNumber() . " [Protocol " . Info::CURRENT_PROTOCOL . "; API " . API_VERSION . "]\r\n"; + $dump .= "Git commit: " . GIT_COMMIT . "\r\n"; + $dump .= "uname -a: " . php_uname("a") . "\r\n"; + $dump .= "PHP Version: " . phpversion() . "\r\n"; + $dump .= "Zend version: " . zend_version() . "\r\n"; + $dump .= "OS : " . PHP_OS . ", " . Utils::getOS() . "\r\n"; + $dump .= "Debug Info: " . var_export($this->debugInfo(false), true) . "\r\n\r\n\r\n"; + global $arguments; + $dump .= "Parameters: " . var_export($arguments, true) . "\r\n\r\n\r\n"; + $p = $this->api->getProperties(); + if($p["rcon.password"] != ""){ + $p["rcon.password"] = "******"; + } + $dump .= "server.properties: " . var_export($p, true) . "\r\n\r\n\r\n"; + if(class_exists("PocketMine\\Plugin\\PluginManager", false)){ + $dump .= "Loaded plugins:\r\n"; + foreach(PluginManager::getPlugins() as $p){ + $d = $p->getDescription(); + $dump .= $d->getName() . " " . $d->getVersion() . " by " . implode(", ", $d->getAuthors()) . "\r\n"; + } + $dump .= "\r\n\r\n"; + } + + $extensions = array(); + foreach(get_loaded_extensions() as $ext){ + $extensions[$ext] = phpversion($ext); + } + + $dump .= "Loaded Modules: " . var_export($extensions, true) . "\r\n"; + $this->checkMemory(); + $dump .= "Memory Usage Tracking: \r\n" . chunk_split(base64_encode(gzdeflate(implode(";", $this->memoryStats), 9))) . "\r\n"; + ob_start(); + phpinfo(); + $dump .= "\r\nphpinfo(): \r\n" . chunk_split(base64_encode(gzdeflate(ob_get_contents(), 9))) . "\r\n"; + ob_end_clean(); + $dump .= "\r\n```"; + $name = "Error_Dump_" . date("D_M_j-H.i.s-T_Y"); + log($dump, $name, true, 0, true); + console("[SEVERE] Please submit the \"{$name}.log\" file to the Bug Reporting page. Give as much info as you can.", true, true, 0); + } + + public function tick(){ + $time = microtime(true); + if($this->lastTick <= ($time - 0.05)){ + $this->tickMeasure[] = $this->lastTick = $time; + unset($this->tickMeasure[key($this->tickMeasure)]); + ++$this->ticks; + + return $this->tickerFunction($time); + } + + return 0; + } + + public static function clientID($ip, $port){ + return crc32($ip . $port) ^ crc32($port . $ip . BOOTUP_RANDOM); + //return $ip . ":" . $port; + } + + public function packetHandler(Packet $packet){ + $data =& $packet; + $CID = Server::clientID($packet->ip, $packet->port); + if(isset(Player::$list[$CID])){ + Player::$list[$CID]->handlePacket($packet); + }else{ + switch($packet->pid()){ + case RakNetInfo::UNCONNECTED_PING: + case RakNetInfo::UNCONNECTED_PING_OPEN_CONNECTIONS: + if($this->invisible === true){ + $pk = new RakNetPacket(RakNetInfo::UNCONNECTED_PONG); + $pk->pingID = $packet->pingID; + $pk->serverID = $this->serverID; + $pk->serverType = $this->serverType; + $pk->ip = $packet->ip; + $pk->port = $packet->port; + $this->send($pk); + break; + } + if(!isset($this->custom["times_" . $CID])){ + $this->custom["times_" . $CID] = 0; + } + $ln = 15; + if($this->description == "" or substr($this->description, -1) != " "){ + $this->description .= " "; + } + $txt = substr($this->description, $this->custom["times_" . $CID], $ln); + $txt .= substr($this->description, 0, $ln - strlen($txt)); + $pk = new RakNetPacket(RakNetInfo::UNCONNECTED_PONG); + $pk->pingID = $packet->pingID; + $pk->serverID = $this->serverID; + $pk->serverType = $this->serverType . $this->name . " [" . count(Player::$list) . "/" . $this->maxClients . "] " . $txt; + $pk->ip = $packet->ip; + $pk->port = $packet->port; + $this->send($pk); + $this->custom["times_" . $CID] = ($this->custom["times_" . $CID] + 1) % strlen($this->description); + break; + case RakNetInfo::OPEN_CONNECTION_REQUEST_1: + if($packet->structure !== RakNetInfo::STRUCTURE){ + console("[DEBUG] Incorrect structure #" . $packet->structure . " from " . $packet->ip . ":" . $packet->port, true, true, 2); + $pk = new RakNetPacket(RakNetInfo::INCOMPATIBLE_PROTOCOL_VERSION); + $pk->serverID = $this->serverID; + $pk->ip = $packet->ip; + $pk->port = $packet->port; + $this->send($pk); + }else{ + $pk = new RakNetPacket(RakNetInfo::OPEN_CONNECTION_REPLY_1); + $pk->serverID = $this->serverID; + $pk->mtuSize = strlen($packet->buffer); + $pk->ip = $packet->ip; + $pk->port = $packet->port; + $this->send($pk); + } + break; + case RakNetInfo::OPEN_CONNECTION_REQUEST_2: + if($this->invisible === true){ + break; + } + + new Player($packet->clientID, $packet->ip, $packet->port, $packet->mtuSize); //New Session! + $pk = new RakNetPacket(RakNetInfo::OPEN_CONNECTION_REPLY_2); + $pk->serverID = $this->serverID; + $pk->serverPort = $this->port; + $pk->mtuSize = $packet->mtuSize; + $pk->ip = $packet->ip; + $pk->port = $packet->port; + $this->send($pk); + break; + } + } + } + + public function send(Packet $packet){ + return $this->interface->writePacket($packet); + } + + public function process(){ + $lastLoop = 0; + while($this->stop === false){ + $packet = $this->interface->readPacket(); + if($packet instanceof Packet){ + $this->packetHandler($packet); + $lastLoop = 0; + } + if(($ticks = $this->tick()) === 0){ + ++$lastLoop; + if($lastLoop < 16){ + usleep(1); + }elseif($lastLoop < 128){ + usleep(1000); + }elseif($lastLoop < 256){ + usleep(2000); + }else{ + usleep(4000); + } + } + } + } + + public function trigger($event, $data = ""){ + if(isset($this->events[$event])){ + foreach($this->events[$event] as $evid => $ev){ + if(!is_callable($ev)){ + $this->deleteEvent($evid); + continue; + } + if(is_array($ev)){ + $method = $ev[1]; + $ev[0]->$method($data, $event); + }else{ + $ev($data, $event); + } + } + } + } + + public function schedule($ticks, callable $callback, $data = array(), $repeat = false, $eventName = "server.schedule"){ + if(!is_callable($callback)){ + return false; + } + $chcnt = $this->scheduleCnt++; + $this->schedule[$chcnt] = array($callback, $data, $eventName); + $this->query("INSERT INTO actions (ID, interval, last, repeat) VALUES(" . $chcnt . ", " . ($ticks / 20) . ", " . microtime(true) . ", " . (((bool) $repeat) === true ? 1 : 0) . ");"); + + return $chcnt; + } + + public function tickerFunction($time){ + //actions that repeat every x time will go here + $this->preparedSQL->selectActions->reset(); + $this->preparedSQL->selectActions->bindValue(":time", $time, SQLITE3_FLOAT); + $actions = $this->preparedSQL->selectActions->execute(); + + $actionCount = 0; + if($actions instanceof \SQLite3Result){ + while(($action = $actions->fetchArray(SQLITE3_ASSOC)) !== false){ + $cid = $action["ID"]; + $this->preparedSQL->updateAction->reset(); + $this->preparedSQL->updateAction->bindValue(":time", $time, SQLITE3_FLOAT); + $this->preparedSQL->updateAction->bindValue(":id", $cid, SQLITE3_INTEGER); + $this->preparedSQL->updateAction->execute(); + if(!@is_callable($this->schedule[$cid][0])){ + $return = false; + }else{ + ++$actionCount; + $return = call_user_func($this->schedule[$cid][0], $this->schedule[$cid][1], $this->schedule[$cid][2]); + } + + if($action["repeat"] == 0 or $return === false){ + $this->query("DELETE FROM actions WHERE ID = " . $action["ID"] . ";"); + $this->schedule[$cid] = null; + unset($this->schedule[$cid]); + } + } + $actions->finalize(); + } + + return $actionCount; + } + + public function event($event, callable $func){ + if(!is_callable($func)){ + return false; + } + $evid = $this->evCnt++; + if(!isset($this->events[$event])){ + $this->events[$event] = array(); + } + $this->events[$event][$evid] = $func; + $this->eventsID[$evid] = $event; + console("[INTERNAL] Attached " . (is_array($func) ? get_class($func[0]) . "::" . $func[1] : $func) . " to event " . $event . " (ID " . $evid . ")", true, true, 3); + + return $evid; + } + + public function deleteEvent($id){ + $id = (int) $id; + if(isset($this->eventsID[$id])){ + $ev = $this->eventsID[$id]; + $this->eventsID[$id] = null; + unset($this->eventsID[$id]); + $this->events[$ev][$id] = null; + unset($this->events[$ev][$id]); + if(count($this->events[$ev]) === 0){ + unset($this->events[$ev]); + } + } + } + +} diff --git a/src/PocketMine/TimeAPI.php b/src/PocketMine/TimeAPI.php index 1f222b96d..30fdd4fb4 100644 --- a/src/PocketMine/TimeAPI.php +++ b/src/PocketMine/TimeAPI.php @@ -33,7 +33,7 @@ class TimeAPI{ private $server; function __construct(){ - $this->server = ServerAPI::request(); + $this->server = Server::getInstance(); } public function init(){ diff --git a/src/PocketMine/block/Bed.php b/src/PocketMine/block/Bed.php index 329af3308..ca2ebbca0 100644 --- a/src/PocketMine/block/Bed.php +++ b/src/PocketMine/block/Bed.php @@ -23,7 +23,7 @@ namespace PocketMine\Block; use PocketMine\Item\Item; use PocketMine\Network\Protocol\ChatPacket; -use PocketMine\ServerAPI; +use PocketMine\Server; use PocketMine; class Bed extends Transparent{ @@ -35,7 +35,7 @@ class Bed extends Transparent{ } public function onActivate(Item $item, PocketMine\Player $player = null){ - if($player instanceof PocketMine\Player and ServerAPI::request()->api->time->getPhase($this->level) !== "night"){ + if($player instanceof PocketMine\Player and Server::getInstance()->api->time->getPhase($this->level) !== "night"){ $pk = new ChatPacket; $pk->message = "You can only sleep at night"; $player->dataPacket($pk); diff --git a/src/PocketMine/block/Beetroot.php b/src/PocketMine/block/Beetroot.php index cce09b322..66ad98508 100644 --- a/src/PocketMine/block/Beetroot.php +++ b/src/PocketMine/block/Beetroot.php @@ -59,7 +59,7 @@ class Beetroot extends Flowable{ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->isTransparent === true){ //Replace with common break method //TODO - //ServerAPI::request()->api->entity->drop($this, Item::get(BEETROOT_SEEDS, 0, 1)); + //Server::getInstance()->api->entity->drop($this, Item::get(BEETROOT_SEEDS, 0, 1)); $this->level->setBlock($this, new Air(), false, false, true); return Level::BLOCK_UPDATE_NORMAL; diff --git a/src/PocketMine/block/BrownMushroom.php b/src/PocketMine/block/BrownMushroom.php index 40d06b899..6ad7f8fc3 100644 --- a/src/PocketMine/block/BrownMushroom.php +++ b/src/PocketMine/block/BrownMushroom.php @@ -35,7 +35,7 @@ class BrownMushroom extends Flowable{ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->isTransparent === true){ //Replace with common break method //TODO - //ServerAPI::request()->api->entity->drop($this, Item::get($this->id)); + //Server::getInstance()->api->entity->drop($this, Item::get($this->id)); $this->level->setBlock($this, new Air(), false, false, true); return Level::BLOCK_UPDATE_NORMAL; diff --git a/src/PocketMine/block/Cactus.php b/src/PocketMine/block/Cactus.php index bb544ddec..35e9dd895 100644 --- a/src/PocketMine/block/Cactus.php +++ b/src/PocketMine/block/Cactus.php @@ -24,7 +24,7 @@ namespace PocketMine\Block; use PocketMine\Item\Item; use PocketMine\Level\Level; use PocketMine\Math\Vector3 as Vector3; -use PocketMine\ServerAPI; +use PocketMine\Server; use PocketMine; class Cactus extends Transparent{ @@ -39,7 +39,7 @@ class Cactus extends Transparent{ $down = $this->getSide(0); if($down->getID() !== self::SAND and $down->getID() !== self::CACTUS){ //Replace with common break method $this->level->setBlock($this, new Air(), false); - ServerAPI::request()->api->entity->drop($this, Item::get($this->id)); + Server::getInstance()->api->entity->drop($this, Item::get($this->id)); return Level::BLOCK_UPDATE_NORMAL; } diff --git a/src/PocketMine/block/Carpet.php b/src/PocketMine/block/Carpet.php index 8953e47ee..cb9cd1e58 100644 --- a/src/PocketMine/block/Carpet.php +++ b/src/PocketMine/block/Carpet.php @@ -67,7 +67,7 @@ class Carpet extends Flowable{ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->getID() === self::AIR){ //Replace with common break method //TODO - //ServerAPI::request()->api->entity->drop($this, Item::get($this->id, $this->meta, 1)); + //Server::getInstance()->api->entity->drop($this, Item::get($this->id, $this->meta, 1)); $this->level->setBlock($this, new Air(), true, false, true); return Level::BLOCK_UPDATE_NORMAL; diff --git a/src/PocketMine/block/Carrot.php b/src/PocketMine/block/Carrot.php index 5fb6fb8f7..899809933 100644 --- a/src/PocketMine/block/Carrot.php +++ b/src/PocketMine/block/Carrot.php @@ -59,7 +59,7 @@ class Carrot extends Flowable{ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->isTransparent === true){ //Replace with common break method //TODO - //ServerAPI::request()->api->entity->drop($this, Item::get(CARROT, 0, 1)); + //Server::getInstance()->api->entity->drop($this, Item::get(CARROT, 0, 1)); $this->level->setBlock($this, new Air(), false, false, true); return Level::BLOCK_UPDATE_NORMAL; diff --git a/src/PocketMine/block/CyanFlower.php b/src/PocketMine/block/CyanFlower.php index 6bd48c3ae..ca78382db 100644 --- a/src/PocketMine/block/CyanFlower.php +++ b/src/PocketMine/block/CyanFlower.php @@ -46,7 +46,7 @@ class CyanFlower extends Flowable{ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->isTransparent === true){ //Replace with common break method //TODO - //ServerAPI::request()->api->entity->drop($this, Item::get($this->id)); + //Server::getInstance()->api->entity->drop($this, Item::get($this->id)); $this->level->setBlock($this, new Air(), false, false, true); return Level::BLOCK_UPDATE_NORMAL; diff --git a/src/PocketMine/block/Dandelion.php b/src/PocketMine/block/Dandelion.php index f9158d8ac..e9e9605b8 100644 --- a/src/PocketMine/block/Dandelion.php +++ b/src/PocketMine/block/Dandelion.php @@ -46,7 +46,7 @@ class Dandelion extends Flowable{ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->isTransparent === true){ //Replace with common break method //TODO - //ServerAPI::request()->api->entity->drop($this, Item::get($this->id)); + //Server::getInstance()->api->entity->drop($this, Item::get($this->id)); $this->level->setBlock($this, new Air(), false, false, true); return Level::BLOCK_UPDATE_NORMAL; diff --git a/src/PocketMine/block/Fallable.php b/src/PocketMine/block/Fallable.php index 68ac07ef5..1a77e363c 100644 --- a/src/PocketMine/block/Fallable.php +++ b/src/PocketMine/block/Fallable.php @@ -23,7 +23,7 @@ namespace PocketMine\Block; use PocketMine\Item\Item; use PocketMine\Level\Level; -use PocketMine\ServerAPI; +use PocketMine\Server; use PocketMine; class Fallable extends Solid{ @@ -35,7 +35,7 @@ class Fallable extends Solid{ public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, PocketMine\Player $player = null){ $ret = $this->level->setBlock($this, $this, true, false, true); - ServerAPI::request()->api->block->blockUpdate(clone $this, Level::BLOCK_UPDATE_NORMAL); + Server::getInstance()->api->block->blockUpdate(clone $this, Level::BLOCK_UPDATE_NORMAL); return $ret; } diff --git a/src/PocketMine/block/Generic.php b/src/PocketMine/block/Generic.php index 4cc4ae0c7..bfca117ba 100644 --- a/src/PocketMine/block/Generic.php +++ b/src/PocketMine/block/Generic.php @@ -23,7 +23,7 @@ namespace PocketMine\Block; use PocketMine\Item\Item; use PocketMine\Level\Level; -use PocketMine\ServerAPI; +use PocketMine\Server; use PocketMine; class Generic extends Block{ @@ -59,7 +59,7 @@ class Generic extends Block{ "z" => $this->z + 0.5, "Tile" => $this->id, ); - $server = ServerAPI::request(); + $server = Server::getInstance(); $this->level->setBlock($this, new Air(), false, false, true); //TODO //$e = $server->api->entity->add($this->level, ENTITY_FALLING, FALLING_SAND, $data); diff --git a/src/PocketMine/block/Ladder.php b/src/PocketMine/block/Ladder.php index 58d88174f..ef02dc064 100644 --- a/src/PocketMine/block/Ladder.php +++ b/src/PocketMine/block/Ladder.php @@ -55,7 +55,7 @@ class Ladder extends Transparent{ public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ /*if($this->getSide(0)->getID() === self::AIR){ //Replace with common break method - ServerAPI::request()->api->entity->drop($this, Item::get(LADDER, 0, 1)); + Server::getInstance()->api->entity->drop($this, Item::get(LADDER, 0, 1)); $this->level->setBlock($this, new Air(), true, true, true); return Level::BLOCK_UPDATE_NORMAL; }*/ diff --git a/src/PocketMine/block/Lava.php b/src/PocketMine/block/Lava.php index 27e9640a1..5a84cd1ea 100644 --- a/src/PocketMine/block/Lava.php +++ b/src/PocketMine/block/Lava.php @@ -24,7 +24,7 @@ namespace PocketMine\Block; use PocketMine\Item\Item; use PocketMine\Level\Level; use PocketMine\Level\Position; -use PocketMine\ServerAPI; +use PocketMine\Server; use PocketMine; class Lava extends Liquid{ @@ -35,7 +35,7 @@ class Lava extends Liquid{ public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, PocketMine\Player $player = null){ $ret = $this->level->setBlock($this, $this, true, false, true); - ServerAPI::request()->api->block->scheduleBlockUpdate(clone $this, 40, Level::BLOCK_UPDATE_NORMAL); + Server::getInstance()->api->block->scheduleBlockUpdate(clone $this, 40, Level::BLOCK_UPDATE_NORMAL); return $ret; } @@ -104,7 +104,7 @@ class Lava extends Liquid{ if($level !== 0x07){ if($down instanceof Air || $down instanceof Lava){ $this->level->setBlock($down, new Lava(0x01), false, false, true); - ServerAPI::request()->api->block->scheduleBlockUpdate(new Position($down, 0, 0, $this->level), 40, Level::BLOCK_UPDATE_NORMAL); + Server::getInstance()->api->block->scheduleBlockUpdate(new Position($down, 0, 0, $this->level), 40, Level::BLOCK_UPDATE_NORMAL); }else{ for($side = 2; $side <= 5; ++$side){ $b = $this->getSide($side); @@ -112,7 +112,7 @@ class Lava extends Liquid{ }elseif($b->isFlowable === true){ $this->level->setBlock($b, new Lava(min($level + 2, 7)), false, false, true); - ServerAPI::request()->api->block->scheduleBlockUpdate(Position::fromObject($b, $this->level), 40, Level::BLOCK_UPDATE_NORMAL); + Server::getInstance()->api->block->scheduleBlockUpdate(Position::fromObject($b, $this->level), 40, Level::BLOCK_UPDATE_NORMAL); } } } @@ -126,7 +126,7 @@ class Lava extends Liquid{ if($tlevel != 0x00){ for($s = 0; $s <= 5; $s++){ $ssb = $sb->getSide($s); - ServerAPI::request()->api->block->scheduleBlockUpdate(Position::fromObject($ssb, $this->level), 40, Level::BLOCK_UPDATE_NORMAL); + Server::getInstance()->api->block->scheduleBlockUpdate(Position::fromObject($ssb, $this->level), 40, Level::BLOCK_UPDATE_NORMAL); } $this->level->setBlock($sb, new Air(), false, false, true); } @@ -137,12 +137,12 @@ class Lava extends Liquid{ if($tlevel != 0x00){ for($s = 0; $s <= 5; $s++){ $ssb = $sb->getSide($s); - ServerAPI::request()->api->block->scheduleBlockUpdate(Position::fromObject($ssb, $this->level), 40, Level::BLOCK_UPDATE_NORMAL); + Server::getInstance()->api->block->scheduleBlockUpdate(Position::fromObject($ssb, $this->level), 40, Level::BLOCK_UPDATE_NORMAL); } $this->level->setBlock($b, new Air(), false, false, true); } } - //ServerAPI::request()->api->block->scheduleBlockUpdate(Position::fromObject($b, $this->level), 10, Level::BLOCK_UPDATE_NORMAL); + //Server::getInstance()->api->block->scheduleBlockUpdate(Position::fromObject($b, $this->level), 10, Level::BLOCK_UPDATE_NORMAL); } $this->level->setBlock($this, new Air(), false, false, true); diff --git a/src/PocketMine/block/Leaves.php b/src/PocketMine/block/Leaves.php index 8c2cbecf8..f3218eded 100644 --- a/src/PocketMine/block/Leaves.php +++ b/src/PocketMine/block/Leaves.php @@ -125,11 +125,11 @@ class Leaves extends Transparent{ $this->level->setBlock($this, new Air(), false, false, true); if(mt_rand(1, 20) === 1){ //Saplings //TODO - //ServerAPI::request()->api->entity->drop($this, Item::get(Item::SAPLING, $this->meta & 0x03, 1)); + //Server::getInstance()->api->entity->drop($this, Item::get(Item::SAPLING, $this->meta & 0x03, 1)); } if(($this->meta & 0x03) === self::OAK and mt_rand(1, 200) === 1){ //Apples //TODO - //ServerAPI::request()->api->entity->drop($this, Item::get(Item::APPLE, 0, 1)); + //Server::getInstance()->api->entity->drop($this, Item::get(Item::APPLE, 0, 1)); } return Level::BLOCK_UPDATE_NORMAL; diff --git a/src/PocketMine/block/MelonStem.php b/src/PocketMine/block/MelonStem.php index 363ff2438..320fe1d98 100644 --- a/src/PocketMine/block/MelonStem.php +++ b/src/PocketMine/block/MelonStem.php @@ -47,7 +47,7 @@ class MelonStem extends Flowable{ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->isTransparent === true){ //Replace with common break method //TODO - //ServerAPI::request()->api->entity->drop($this, Item::get(MELON_SEEDS, 0, mt_rand(0, 2))); + //Server::getInstance()->api->entity->drop($this, Item::get(MELON_SEEDS, 0, mt_rand(0, 2))); $this->level->setBlock($this, new Air(), false, false, true); return Level::BLOCK_UPDATE_NORMAL; diff --git a/src/PocketMine/block/Potato.php b/src/PocketMine/block/Potato.php index e83aa2194..5a055a946 100644 --- a/src/PocketMine/block/Potato.php +++ b/src/PocketMine/block/Potato.php @@ -61,7 +61,7 @@ class Potato extends Flowable{ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->isTransparent === true){ //Replace with common break method //TODO - //ServerAPI::request()->api->entity->drop($this, Item::get(POTATO, 0, 1)); + //Server::getInstance()->api->entity->drop($this, Item::get(POTATO, 0, 1)); $this->level->setBlock($this, new Air(), false, false, true); return Level::BLOCK_UPDATE_NORMAL; diff --git a/src/PocketMine/block/PumpkinStem.php b/src/PocketMine/block/PumpkinStem.php index a407efd85..547d28b08 100644 --- a/src/PocketMine/block/PumpkinStem.php +++ b/src/PocketMine/block/PumpkinStem.php @@ -47,7 +47,7 @@ class PumpkinStem extends Flowable{ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->isTransparent === true){ //Replace with common break method //TODO - //ServerAPI::request()->api->entity->drop($this, Item::get(PUMPKIN_SEEDS, 0, mt_rand(0, 2))); + //Server::getInstance()->api->entity->drop($this, Item::get(PUMPKIN_SEEDS, 0, mt_rand(0, 2))); $this->level->setBlock($this, new Air(), false, false, true); return Level::BLOCK_UPDATE_NORMAL; diff --git a/src/PocketMine/block/RedMushroom.php b/src/PocketMine/block/RedMushroom.php index 4997db200..34e37fe35 100644 --- a/src/PocketMine/block/RedMushroom.php +++ b/src/PocketMine/block/RedMushroom.php @@ -35,7 +35,7 @@ class RedMushroom extends Flowable{ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->isTransparent === true){ //Replace with common break method //TODO - //ServerAPI::request()->api->entity->drop($this, Item::get($this->id)); + //Server::getInstance()->api->entity->drop($this, Item::get($this->id)); $this->level->setBlock($this, new Air(), false); return Level::BLOCK_UPDATE_NORMAL; diff --git a/src/PocketMine/block/Sapling.php b/src/PocketMine/block/Sapling.php index 084ff463e..f7cfc4191 100644 --- a/src/PocketMine/block/Sapling.php +++ b/src/PocketMine/block/Sapling.php @@ -75,7 +75,7 @@ class Sapling extends Flowable{ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->isTransparent === true){ //Replace with common break method //TODO - //ServerAPI::request()->api->entity->drop($this, Item::get($this->id)); + //Server::getInstance()->api->entity->drop($this, Item::get($this->id)); $this->level->setBlock($this, new Air(), false, false, true); return Level::BLOCK_UPDATE_NORMAL; diff --git a/src/PocketMine/block/SignPost.php b/src/PocketMine/block/SignPost.php index 416d4120d..acb7e5fcc 100644 --- a/src/PocketMine/block/SignPost.php +++ b/src/PocketMine/block/SignPost.php @@ -61,7 +61,7 @@ class SignPost extends Transparent{ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->getID() === self::AIR){ //Replace with common break method //TODO - //ServerAPI::request()->api->entity->drop($this, Item::get(SIGN, 0, 1)); + //Server::getInstance()->api->entity->drop($this, Item::get(SIGN, 0, 1)); $this->level->setBlock($this, new Air(), true, true, true); return Level::BLOCK_UPDATE_NORMAL; diff --git a/src/PocketMine/block/Sugarcane.php b/src/PocketMine/block/Sugarcane.php index 7faf32310..054028c62 100644 --- a/src/PocketMine/block/Sugarcane.php +++ b/src/PocketMine/block/Sugarcane.php @@ -66,7 +66,7 @@ class Sugarcane extends Flowable{ $down = $this->getSide(0); if($down->isTransparent === true and $down->getID() !== self::SUGARCANE_BLOCK){ //Replace with common break method //TODO - //ServerAPI::request()->api->entity->drop($this, Item::get(SUGARCANE)); + //Server::getInstance()->api->entity->drop($this, Item::get(SUGARCANE)); $this->level->setBlock($this, new Air(), false, false, true); return Level::BLOCK_UPDATE_NORMAL; diff --git a/src/PocketMine/block/TNT.php b/src/PocketMine/block/TNT.php index 5caeaf703..7a0b4e6b0 100644 --- a/src/PocketMine/block/TNT.php +++ b/src/PocketMine/block/TNT.php @@ -45,7 +45,7 @@ class TNT extends Solid{ ); $this->level->setBlock($this, new Air(), false, false, true); //TODO - //$e = ServerAPI::request()->api->entity->add($this->level, ENTITY_OBJECT, OBJECT_PRIMEDTNT, $data); + //$e = Server::getInstance()->api->entity->add($this->level, ENTITY_OBJECT, OBJECT_PRIMEDTNT, $data); //$e->spawnToAll(); return true; diff --git a/src/PocketMine/block/Torch.php b/src/PocketMine/block/Torch.php index 95d34aaba..68f270410 100644 --- a/src/PocketMine/block/Torch.php +++ b/src/PocketMine/block/Torch.php @@ -46,7 +46,7 @@ class Torch extends Flowable{ if($this->getSide($faces[$side])->isTransparent === true and !($side === 0 and $this->getSide(0)->getID() === self::FENCE)){ //Replace with common break method //TODO - //ServerAPI::request()->api->entity->drop($this, Item::get($this->id, 0, 1)); + //Server::getInstance()->api->entity->drop($this, Item::get($this->id, 0, 1)); $this->level->setBlock($this, new Air(), true, false, true); return Level::BLOCK_UPDATE_NORMAL; diff --git a/src/PocketMine/block/Water.php b/src/PocketMine/block/Water.php index 69ba9172d..d4d413349 100644 --- a/src/PocketMine/block/Water.php +++ b/src/PocketMine/block/Water.php @@ -25,7 +25,7 @@ use PocketMine\Item\Item; use PocketMine\Level\Level; use PocketMine\Level\Position; use PocketMine; -use PocketMine\ServerAPI; +use PocketMine\Server; class Water extends Liquid{ public function __construct($meta = 0){ @@ -35,7 +35,7 @@ class Water extends Liquid{ public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, PocketMine\Player $player = null){ $ret = $this->level->setBlock($this, $this, true, false, true); - ServerAPI::request()->api->block->scheduleBlockUpdate(clone $this, 10, Level::BLOCK_UPDATE_NORMAL); + Server::getInstance()->api->block->scheduleBlockUpdate(clone $this, 10, Level::BLOCK_UPDATE_NORMAL); return $ret; } @@ -110,7 +110,7 @@ class Water extends Liquid{ if($level !== 0x07){ if($down instanceof Air || $down instanceof Water){ $this->level->setBlock($down, new Water(0x01), false, false, true); - ServerAPI::request()->api->block->scheduleBlockUpdate(Position::fromObject($down, $this->level), 10, Level::BLOCK_UPDATE_NORMAL); + Server::getInstance()->api->block->scheduleBlockUpdate(Position::fromObject($down, $this->level), 10, Level::BLOCK_UPDATE_NORMAL); }else{ for($side = 2; $side <= 5; ++$side){ $b = $this->getSide($side); @@ -120,7 +120,7 @@ class Water extends Liquid{ } }elseif($b->isFlowable === true){ $this->level->setBlock($b, new Water($level + 1), false, false, true); - ServerAPI::request()->api->block->scheduleBlockUpdate(Position::fromObject($b, $this->level), 10, Level::BLOCK_UPDATE_NORMAL); + Server::getInstance()->api->block->scheduleBlockUpdate(Position::fromObject($b, $this->level), 10, Level::BLOCK_UPDATE_NORMAL); } } } @@ -134,7 +134,7 @@ class Water extends Liquid{ if($tlevel != 0x00){ for($s = 0; $s <= 5; $s++){ $ssb = $sb->getSide($s); - ServerAPI::request()->api->block->scheduleBlockUpdate(Position::fromObject($ssb, $this->level), 10, Level::BLOCK_UPDATE_NORMAL); + Server::getInstance()->api->block->scheduleBlockUpdate(Position::fromObject($ssb, $this->level), 10, Level::BLOCK_UPDATE_NORMAL); } $this->level->setBlock($sb, new Air(), false, false, true); } @@ -145,12 +145,12 @@ class Water extends Liquid{ if($tlevel != 0x00){ for($s = 0; $s <= 5; $s++){ $ssb = $sb->getSide($s); - ServerAPI::request()->api->block->scheduleBlockUpdate(Position::fromObject($ssb, $this->level), 10, Level::BLOCK_UPDATE_NORMAL); + Server::getInstance()->api->block->scheduleBlockUpdate(Position::fromObject($ssb, $this->level), 10, Level::BLOCK_UPDATE_NORMAL); } $this->level->setBlock($b, new Air(), false, false, true); } } - //ServerAPI::request()->api->block->scheduleBlockUpdate(Position::fromObject($b, $this->level), 10, Level::BLOCK_UPDATE_NORMAL); + //Server::getInstance()->api->block->scheduleBlockUpdate(Position::fromObject($b, $this->level), 10, Level::BLOCK_UPDATE_NORMAL); } $this->level->setBlock($this, new Air(), false, false, true); } diff --git a/src/PocketMine/block/Wheat.php b/src/PocketMine/block/Wheat.php index 60f918616..f51539d96 100644 --- a/src/PocketMine/block/Wheat.php +++ b/src/PocketMine/block/Wheat.php @@ -61,7 +61,7 @@ class Wheat extends Flowable{ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->isTransparent === true){ //Replace with common break method //TODO - //ServerAPI::request()->api->entity->drop($this, Item::get(WHEAT_SEEDS, 0, 1)); + //Server::getInstance()->api->entity->drop($this, Item::get(WHEAT_SEEDS, 0, 1)); $this->level->setBlock($this, new Air(), false, false, true); return Level::BLOCK_UPDATE_NORMAL; diff --git a/src/PocketMine/command/Command.php b/src/PocketMine/command/Command.php new file mode 100644 index 000000000..07dfbaaf6 --- /dev/null +++ b/src/PocketMine/command/Command.php @@ -0,0 +1,296 @@ +name = $name; + $this->nextLabel = $name; + $this->label = $name; + $this->description = $description; + $this->usageMessage = $usageMessage === null ? "/" . $name : $usageMessage; + $this->aliases = $aliases; + $this->activeAliases = (array) $aliases; + } + + /** + * @param CommandSender $sender + * @param string $commandLabel + * @param string[] $args + * + * @return mixed + */ + public abstract function execute(CommandSender $sender, $commandLabel, array $args); + + /** + * @return string + */ + public function getName(){ + return $this->name; + } + + /** + * @return string + */ + public function getPermission(){ + return $this->permission; + } + + /** + * @param string|null $permission + */ + public function setPermission($permission){ + $this->permission = $permission; + } + + /** + * @param CommandSender $target + * + * @return bool + */ + public function testPermission(CommandSender $target){ + if($this->testPermissionSilent($target)){ + return true; + } + + if($this->permissionMessage === null){ + $target->sendMessage(TextFormat::RED . "You don't have permissions to use this command."); + }elseif($this->permissionMessage !== ""){ + $target->sendMessage(str_replace("", $this->permission, $this->permissionMessage)); + } + + return false; + } + + /** + * @param CommandSender $target + * + * @return bool + */ + public function testPermissionSilent(CommandSender $target){ + if($this->permission === null or $this->permission === ""){ + return true; + } + + foreach(explode(";", $this->permission) as $permission){ + if($target->hasPermission($permission)){ + return true; + } + } + + return false; + } + + /** + * @return string + */ + public function getLabel(){ + return $this->label; + } + + public function setLabel($name){ + $this->nextLabel = $name; + if(!$this->isRegistered()){ + $this->label = $name; + return true; + } + return false; + } + + /** + * Registers the command into a Command map + * + * @param CommandMap $commandMap + * + * @return bool + */ + public function register(CommandMap $commandMap){ + if($this->allowChangesFrom($commandMap)){ + $this->commandMap = $commandMap; + return true; + } + return false; + } + + /** + * @param CommandMap $commandMap + * + * @return bool + */ + public function unregister(CommandMap $commandMap){ + if($this->allowChangesFrom($commandMap)){ + $this->commandMap = null; + $this->activeAliases = $this->aliases; + $this->label = $this->nextLabel; + return true; + } + return false; + } + + /** + * @param CommandMap $commandMap + * + * @return bool + */ + private function allowChangesFrom(CommandMap $commandMap){ + return $this->commandMap === null or $this->commandMap === $commandMap; + } + + /** + * @return bool + */ + public function isRegistered(){ + return $this->commandMap !== null; + } + + /** + * @return string[] + */ + public function getAliases(){ + return $this->activeAliases; + } + + /** + * @return string + */ + public function getPermissionMessage(){ + return $this->permissionMessage; + } + + /** + * @return string + */ + public function getDescription(){ + return $this->description; + } + + /** + * @return string + */ + public function getUsage(){ + return $this->usageMessage; + } + + /** + * @param string[] $aliases + */ + public function setAliases(array $aliases){ + $this->aliases = $aliases; + if(!$this->isRegistered()){ + $this->activeAliases = (array) $aliases; + } + } + + /** + * @param string $description + */ + public function setDescription($description){ + $this->description = $description; + } + + /** + * @param string $permissionMessage + */ + public function setPermissionMessage($permissionMessage){ + $this->permissionMessage = $permissionMessage; + } + + /** + * @param string $usage + */ + public function setUsage($usage){ + $this->usageMessage = $usage; + } + + /** + * TODO: static::broadcastCommandMessage() + * + * @param CommandSender $source + * @param string $message + * @param bool $sendToSource + */ + public static function broadcastCommandMessage(CommandSender $source, $message, $sendToSource = true){ + + } +} \ No newline at end of file diff --git a/src/PocketMine/command/CommandExecutor.php b/src/PocketMine/command/CommandExecutor.php new file mode 100644 index 000000000..baae52de5 --- /dev/null +++ b/src/PocketMine/command/CommandExecutor.php @@ -0,0 +1,38 @@ +perm = new PermissibleBase($this); + } + + /** + * @param PocketMine\Permission\Permission|string $name + * + * @return bool + */ + public function isPermissionSet($name){ + return $this->perm->isPermissionSet($name); + } + + /** + * @param PocketMine\Permission\Permission|string $name + * + * @return bool + */ + public function hasPermission($name){ + return $this->perm->hasPermission($name); + } + + /** + * @param Plugin $plugin + * @param string $name + * @param bool $value + * + * @return PocketMine\Permission\PermissionAttachment + */ + public function addAttachment(Plugin $plugin, $name = null, $value = null){ + return $this->perm->addAttachment($plugin, $name, $value); + } + + /** + * @param PermissionAttachment $attachment + * + * @return void + */ + public function removeAttachment(PermissionAttachment $attachment){ + $this->perm->removeAttachment($attachment); + } + + public function recalculatePermissions(){ + $this->perm->recalculatePermissions(); + } + + /** + * @return PocketMine\Permission\PermissionAttachmentInfo[] + */ + public function getEffectivePermissions(){ + return $this->perm->getEffectivePermissions(); + } + + /** + * @return bool + */ + public function isPlayer(){ + return false; + } + + /** + * @return PocketMine\Server + */ + public function getServer(){ + return PocketMine\Server::getInstance(); + } + + /** + * @param string $message + */ + public function sendMessage($message){ + foreach(explode("\n", $message) as $line){ + $line = trim($line); + console($line); + } + } + + /** + * @return string + */ + public function getName(){ + return "CONSOLE"; + } + + /** + * @return bool + */ + public function isOp(){ + return true; + } + + /** + * @param bool $value + */ + public function setOp($value){ + + } + +} \ No newline at end of file diff --git a/src/PocketMine/command/PluginCommand.php b/src/PocketMine/command/PluginCommand.php new file mode 100644 index 000000000..7c9d6dbda --- /dev/null +++ b/src/PocketMine/command/PluginCommand.php @@ -0,0 +1,71 @@ +owningPlugin = $owner; + $this->executor = $owner; + $this->usageMessage = ""; + } + + public function execute(CommandSender $sender, $commandLabel, array $args){ + + if(!$this->owningPlugin->isEnabled()){ + return false; + } + + if(!$this->testPermission($sender)){ + return false; + } + + $success = $this->executor->onCommand($sender, $this, $commandLabel, $args); + + if(!$success and $this->usageMessage !== ""){ + $sender->sendMessage($this->usageMessage); + } + + return $success; + } + + /** + * @return Plugin + */ + public function getPlugin(){ + return $this->owningPlugin; + } +} \ No newline at end of file diff --git a/src/PocketMine/command/RemoteConsoleCommandSender.php b/src/PocketMine/command/RemoteConsoleCommandSender.php new file mode 100644 index 000000000..103c23924 --- /dev/null +++ b/src/PocketMine/command/RemoteConsoleCommandSender.php @@ -0,0 +1,28 @@ +server = $server; + $this->setDefaultCommands(); + } + + private function setDefaultCommands(){ + //TODO + $this->register("pocketmine", new VersionCommand("version")); + } + + + public function registerAll($fallbackPrefix, array $commands){ + foreach($commands as $command){ + $this->register($fallbackPrefix, $command); + } + } + + public function register($fallbackPrefix, Command $command, $label = null){ + if($label === null){ + $label = $command->getName(); + } + $label = strtolower(trim($label)); + $fallbackPrefix = strtolower(trim($fallbackPrefix)); + + $registered = $this->registerAlias($command, false, $fallbackPrefix, $label); + + $aliases = $command->getAliases(); + foreach($aliases as $index => $alias){ + if(!$this->registerAlias($command, true, $fallbackPrefix, $alias)){ + unset($aliases[$index]); + } + } + $command->setAliases($aliases); + + if(!$registered){ + $command->setLabel($fallbackPrefix . ":" . $label); + } + + $command->register($this); + + return $registered; + } + + private function registerAlias(Command $command, $isAlias, $fallbackPrefix, $label){ + $this->knownCommands[$fallbackPrefix . ":" . $label] = $command; + if(($command instanceof VanillaCommand or $isAlias) and isset($this->knownCommands[$label])){ + return false; + } + + if(isset($this->knownCommands[$label]) and $this->knownCommands[$label]->getLabel() === $label){ + return false; + } + + if(!$isAlias){ + $command->setLabel($label); + } + + $this->knownCommands[$label] = $command; + + return true; + } + + public function dispatch(CommandSender $sender, $commandLine){ + $args = explode(" ", $commandLine); + + if(count($args) === 0){ + return false; + } + + $sentCommandLabel = strtolower(array_shift($args)); + $target = $this->getCommand($sentCommandLabel); + + if($target === null){ + return false; + } + + $target->execute($sender, $sentCommandLabel, $args); + + return true; + } + + public function clearCommands(){ + foreach($this->knownCommands as $command){ + $command->unregister($this); + } + $this->knownCommands = array(); + $this->setDefaultCommands(); + } + + public function getCommand($name){ + if(isset($this->knownCommands[$name])){ + return $this->knownCommands[$name]; + } + return null; + } + + /** + * @return Command[] + */ + public function getCommands(){ + return $this->knownCommands; + } + + + /** + * @return void + */ + public function registerServerAliases(){ + //TODO + } + + +} \ No newline at end of file diff --git a/src/PocketMine/command/defaults/VanillaCommand.php b/src/PocketMine/command/defaults/VanillaCommand.php new file mode 100644 index 000000000..5ecf779a8 --- /dev/null +++ b/src/PocketMine/command/defaults/VanillaCommand.php @@ -0,0 +1,68 @@ + $max){ + $i = $max; + } + + return $i; + } + + protected function getRelativeDouble($original, CommandSender $sender, $input){ + if($input{0} === "~"){ + $value = $this->getDouble($sender, substr($input, 1)); + return $original + $value; + } + return $this->getDouble($input); + } + + protected function getDouble(CommandSender $sender, $value, $min = self::MIN_COORD, $max = self::MAX_COORD){ + $i = (double) $value; + + if($i < $min){ + $i = $min; + }elseif($i > $max){ + $i = $max; + } + + return $i; + } +} \ No newline at end of file diff --git a/src/PocketMine/command/defaults/VersionCommand.php b/src/PocketMine/command/defaults/VersionCommand.php new file mode 100644 index 000000000..856a31c45 --- /dev/null +++ b/src/PocketMine/command/defaults/VersionCommand.php @@ -0,0 +1,97 @@ +setPermission("pocketmine.command.version"); + } + + public function execute(CommandSender $sender, $currentAlias, array $args){ + if(!$this->testPermission($sender)){ + return true; + } + + if(count($args) === 0){ + $output = "This server is running PocketMine-MP version " . PocketMine\Server::getInstance()->getPocketMineVersion() . " 「" . PocketMine\Server::getInstance()->getCodename() . "」 (Implementing API version " . PocketMine\Server::getInstance()->getApiVersion() . " for Minecraft: PE " . PocketMine\Server::getInstance()->getVersion() . " protocol version " . PocketMine\Network\Protocol\Info::CURRENT_PROTOCOL . ")"; + if(\PocketMine\GIT_COMMIT !== str_repeat("00", 20)){ + $output .= " [git " . \PocketMine\GIT_COMMIT . "]"; + } + $sender->sendMessage($output); + }else{ + $pluginName = implode(" ", $args); + $exactPlugin = PocketMine\Server::getInstance()->getPluginManager()->getPlugin($pluginName); + + if($exactPlugin instanceof PocketMine\Plugin\Plugin){ + $this->describeToSender($exactPlugin, $sender); + return true; + } + + $found = false; + $pluginName = strtolower($pluginName); + foreach(PocketMine\Server::getInstance()->getPluginManager()->getPlugins() as $plugin){ + if(stripos($plugin->getName(), $pluginName) !== false){ + $this->describeToSender($plugin, $sender); + $found = true; + } + } + + if(!$found){ + $sender->sendMessage("This server is not running any plugin by that name.\nUse /plugins to get a list of plugins."); + } + } + + return true; + } + + private function describeToSender(PocketMine\Plugin\Plugin $plugin, CommandSender $sender){ + $desc = $plugin->getDescription(); + $sender->sendMessage(TextFormat::GREEN . $desc->getName() . TextFormat::WHITE . " version " . TextFormat::GREEN . $desc->getVersion()); + + if($desc->getDescription() != null){ + $sender->sendMessage($desc->getDescription()); + } + + if($desc->getWebsite() != null){ + $sender->sendMessage("Website: " . $desc->getWebsite()); + } + + if(count($authors = $desc->getAuthors()) > 0){ + if(count($authors) === 1){ + $sender->sendMessage("Author: " . implode(", ", $authors)); + }else{ + $sender->sendMessage("Authors: " . implode(", ", $authors)); + } + } + } +} \ No newline at end of file diff --git a/src/PocketMine/entity/Entity.php b/src/PocketMine/entity/Entity.php index 2bbfaae79..08f3a5b55 100644 --- a/src/PocketMine/entity/Entity.php +++ b/src/PocketMine/entity/Entity.php @@ -45,9 +45,20 @@ use PocketMine; abstract class Entity extends Position{ public static $entityCount = 1; + + /** + * @var Entity[] + */ public static $list = array(); + + /** + * @var Entity[] + */ public static $needUpdate = array(); + /** + * @var Player[] + */ protected $hasSpawned = array(); protected $id; diff --git a/src/PocketMine/item/Painting.php b/src/PocketMine/item/Painting.php index f88887dcc..df85cf62e 100644 --- a/src/PocketMine/item/Painting.php +++ b/src/PocketMine/item/Painting.php @@ -25,7 +25,7 @@ use PocketMine\Block\Block; use PocketMine; use PocketMine\Level\Level; use PocketMine\Player; -use PocketMine\ServerAPI; +use PocketMine\Server; class Painting extends Item{ public function __construct($meta = 0, $count = 1){ @@ -35,7 +35,7 @@ class Painting extends Item{ public function onActivate(Level $level, Player $player, Block $block, Block $target, $face, $fx, $fy, $fz){ if($target->isTransparent === false and $face > 1 and $block->isSolid === false){ - $server = ServerAPI::request(); + $server = Server::getInstance(); $faces = array( 2 => 1, 3 => 3, diff --git a/src/PocketMine/item/SpawnEgg.php b/src/PocketMine/item/SpawnEgg.php index f47d8bc32..d14d50536 100644 --- a/src/PocketMine/item/SpawnEgg.php +++ b/src/PocketMine/item/SpawnEgg.php @@ -46,8 +46,8 @@ class SpawnEgg extends Item{ "y" => $block->y, "z" => $block->z + 0.5, ); - //$e = ServerAPI::request()->api->entity->add($block->level, ENTITY_MOB, $this->meta, $data); - //ServerAPI::request()->api->entity->spawnToAll($e); + //$e = Server::getInstance()->api->entity->add($block->level, ENTITY_MOB, $this->meta, $data); + //Server::getInstance()->api->entity->spawnToAll($e); if(($player->gamemode & 0x01) === 0){ --$this->count; } diff --git a/src/PocketMine/level/Level.php b/src/PocketMine/level/Level.php index 4999975dc..0d424b9d0 100644 --- a/src/PocketMine/level/Level.php +++ b/src/PocketMine/level/Level.php @@ -43,11 +43,12 @@ use PocketMine\Network\Protocol\SetTimePacket; use PocketMine\Network\Protocol\UpdateBlockPacket; use PocketMine\Player; use PocketMine\PMF\LevelFormat; -use PocketMine\ServerAPI; +use PocketMine\Server; use PocketMine\Tile\Chest; use PocketMine\Tile\Furnace; use PocketMine\Tile\Sign; use PocketMine\Tile\Tile; +use PocketMine\Entity\Entity; use PocketMine\Utils\Cache; use PocketMine\Utils\Config; use PocketMine\Utils\Random; @@ -66,15 +67,39 @@ class Level{ const BLOCK_UPDATE_WEAK = 4; const BLOCK_UPDATE_TOUCH = 5; - protected static $list = array(); + /** + * @var Level[] + */ + public static $list = array(); + + /** + * @var Level + */ public static $default = null; + /** + * @var Player[] + */ public $players = array(); + /** + * @var Entity[] + */ public $entities = array(); + + /** + * @var Entity[][] + */ public $chunkEntities = array(); + /** + * @var Tile[] + */ public $tiles = array(); + + /** + * @var Tile[][] + */ public $chunkTiles = array(); public $nextSave; @@ -84,13 +109,22 @@ class Level{ */ public $level; public $stopTime; - private $time, $startCheck, $startTime, $server, $name, $usedChunks, $changedBlocks, $changedCount, $generator; + private $time, $startCheck, $startTime; + /** + * @var Server + */ + private $server; + private $name, $usedChunks, $changedBlocks, $changedCount, $generator; public static function init(){ if(self::$default === null){ - $default = ServerAPI::request()->api->getProperty("level-name"); + $default = Server::getInstance()->getConfigString("level-name", null); + if($default == ""){ + trigger_error("level-name cannot be null", E_USER_ERROR); + return; + } if(self::loadLevel($default) === false){ - self::generateLevel($default, ServerAPI::request()->seed); + self::generateLevel($default, 0); //TODO: Server->getSeed(); self::loadLevel($default); } self::$default = self::get($default); @@ -264,7 +298,7 @@ class Level{ } foreach($blockUpdates->getAll() as $bupdate){ - ServerAPI::request()->api->block->scheduleBlockUpdate(new Position((int) $bupdate["x"], (int) $bupdate["y"], (int) $bupdate["z"], $level), (float) $bupdate["delay"], (int) $bupdate["type"]); + Server::getInstance()->api->block->scheduleBlockUpdate(new Position((int) $bupdate["x"], (int) $bupdate["y"], (int) $bupdate["z"], $level), (float) $bupdate["delay"], (int) $bupdate["type"]); } return true; @@ -285,14 +319,14 @@ class Level{ return false; } $options = array(); - if($options === false and ServerAPI::request()->api->getProperty("generator-settings") !== false and trim(ServerAPI::request()->api->getProperty("generator-settings")) != ""){ - $options["preset"] = ServerAPI::request()->api->getProperty("generator-settings"); + if($options === false and trim(Server::getInstance()->getConfigString("generator-settings", "")) !== ""){ + $options["preset"] = Server::getInstance()->getConfigString("generator-settings", ""); } if($generator !== false and class_exists($generator)){ $generator = new $generator($options); }else{ - if(strtoupper(ServerAPI::request()->api->getProperty("level-type")) == "FLAT"){ + if(strtoupper(Server::getInstance()->getLevelType()) == "FLAT"){ $generator = new Flat($options); }else{ $generator = new Normal($options); @@ -332,15 +366,13 @@ class Level{ } public function __construct(LevelFormat $level, $name){ - $this->server = ServerAPI::request(); + $this->server = Server::getInstance(); $this->level = $level; $this->level->level = $this; $this->startTime = $this->time = (int) $this->level->getData("time"); $this->nextSave = $this->startCheck = microtime(true); $this->nextSave += 90; $this->stopTime = false; - $this->server->schedule(1, array($this, "doTick"), array(), true); - $this->server->schedule(20 * 13, array($this, "checkTime"), array(), true); $this->name = $name; $this->usedChunks = array(); $this->changedBlocks = array(); @@ -372,7 +404,7 @@ class Level{ $this->save(); foreach($this->getPlayers() as $player){ if($this === self::getDefault()){ - $player->close($player->getUsername() . " has left the game", "forced level unload"); + $player->close($player->getName() . " has left the game", "forced level unload"); }else{ $player->teleport(Level::getDefault()->getSafeSpawn()); } @@ -436,6 +468,10 @@ class Level{ return false; } + if(($this->server->getTick() % 200) === 0){ + $this->checkTime(); + } + if($this->level->isGenerating === 0 and count($this->changedCount) > 0){ foreach($this->changedCount as $index => $mini){ for($Y = 0; $Y < 8; ++$Y){ @@ -536,9 +572,10 @@ class Level{ if(!isset($this->level)){ return false; } - if($this->server->saveEnabled === false and $force === false){ + //TODO: save enabled/disabled + /*if($this->server->saveEnabled === false and $force === false){ return; - } + }*/ if($extra !== false){ $this->doSaveRoundExtra(); @@ -805,7 +842,7 @@ class Level{ new String("Text4", "") ))); if($player instanceof Player){ - $tile->namedtag->creator = new String("creator", $player->getUsername()); + $tile->namedtag->creator = new String("creator", $player->getName()); } } $item->setCount($item->getCount() - 1); diff --git a/src/PocketMine/network/Handler.php b/src/PocketMine/network/Handler.php index fb94d4035..2801bc6cc 100644 --- a/src/PocketMine/network/Handler.php +++ b/src/PocketMine/network/Handler.php @@ -33,7 +33,7 @@ use PocketMine\Network\Query\QueryHandler; use PocketMine\Network\Query\QueryPacket; use PocketMine\Network\RakNet\Info; use PocketMine\Network\RakNet\Packet; -use PocketMine\ServerAPI; +use PocketMine\Server; class Handler{ public $bandwidth; @@ -77,7 +77,7 @@ class Handler{ } return $packet; - }elseif($pid === 0xfe and $buffer{1} === "\xfd" and ServerAPI::request()->api->query instanceof QueryHandler){ + }elseif($pid === 0xfe and $buffer{1} === "\xfd" and Server::getInstance()->api->query instanceof QueryHandler){ $packet = new QueryPacket; $packet->ip = $source; $packet->port = $port; @@ -85,7 +85,7 @@ class Handler{ if(EventHandler::callEvent(new PacketReceiveEvent($packet)) === Event::DENY){ return false; } - ServerAPI::request()->api->query->handle($packet); + Server::getInstance()->api->query->handle($packet); }else{ $packet = new Packet($pid); $packet->ip = $source; diff --git a/src/PocketMine/network/query/QueryHandler.php b/src/PocketMine/network/query/QueryHandler.php index 2409f0309..137fdc1da 100644 --- a/src/PocketMine/network/query/QueryHandler.php +++ b/src/PocketMine/network/query/QueryHandler.php @@ -28,7 +28,7 @@ namespace PocketMine\Network\Query; use PocketMine; use PocketMine\Level\Level; use PocketMine\Player; -use PocketMine\ServerAPI; +use PocketMine\Server; use PocketMine\Utils\Utils; class QueryHandler{ @@ -36,9 +36,9 @@ class QueryHandler{ public function __construct(){ console("[INFO] Starting GS4 status listener"); - $this->server = ServerAPI::request(); - $addr = ($ip = $this->server->api->getProperty("server-ip")) != "" ? $ip : "0.0.0.0"; - $port = $this->server->api->getProperty("server-port"); + $this->server = Server::getInstance(); + $addr = ($ip = $this->server->getIp()) != "" ? $ip : "0.0.0.0"; + $port = $this->server->getPort(); console("[INFO] Setting query port to $port"); /* The Query protocol is built on top of the existing Minecraft PE UDP network stack. @@ -59,7 +59,7 @@ class QueryHandler{ public function regenerateInfo(){ $str = ""; $plist = "PocketMine-MP " . PocketMine\VERSION; - $pl = PocketMine\Plugin\PluginManager::getPlugins(); + $pl = $this->server->getPluginManager()->getPlugins(); if(count($pl) > 0){ $plist .= ":"; foreach($pl as $p){ @@ -70,26 +70,25 @@ class QueryHandler{ } $KVdata = array( "splitnum" => chr(128), - "hostname" => $this->server->name, - "gametype" => ($this->server->gamemode & 0x01) === 0 ? "SMP" : "CMP", + "hostname" => $this->server->getServerName(), + "gametype" => ($this->server->getGamemode() & 0x01) === 0 ? "SMP" : "CMP", "game_id" => "MINECRAFTPE", - "version" => PocketMine\MINECRAFT_VERSION, - "server_engine" => "PocketMine-MP " . PocketMine\VERSION, + "version" => $this->server->getVersion(), + "server_engine" => $this->server->getName() . " " . $this->server->getPocketMineVersion(), "plugins" => $plist, "map" => Level::getDefault()->getName(), "numplayers" => count(Player::$list), - "maxplayers" => $this->server->maxClients, - "whitelist" => $this->server->api->getProperty("white-list") === true ? "on" : "off", - "hostport" => $this->server->api->getProperty("server-port"), - //"hostip" => $this->server->api->getProperty("server-ip", "0.0.0.0") + "maxplayers" => $this->server->getMaxPlayers(), + "whitelist" => $this->server->hasWhitelist() === true ? "on" : "off", + "hostport" => $this->server->getPort() ); foreach($KVdata as $key => $value){ $str .= $key . "\x00" . $value . "\x00"; } $str .= "\x00\x01player_\x00\x00"; foreach(Player::$list as $player){ - if($player->getUsername() != ""){ - $str .= $player->getUsername() . "\x00"; + if($player->getName() != ""){ + $str .= $player->getName() . "\x00"; } } $str .= "\x00"; @@ -135,7 +134,7 @@ class QueryHandler{ } $pk->payload = $this->longData; }else{ - $pk->payload = $this->server->name . "\x00" . (($this->server->gamemode & 0x01) === 0 ? "SMP" : "CMP") . "\x00" . Level::getDefault()->getName() . "\x00" . count(Player::$list) . "\x00" . $this->server->maxClients . "\x00" . Utils::writeLShort($this->server->api->getProperty("server-port")) . $this->server->api->getProperty("server-ip", "0.0.0.0") . "\x00"; + $pk->payload = $this->server->getServerName() . "\x00" . (($this->server->getGamemode() & 0x01) === 0 ? "SMP" : "CMP") . "\x00" . Level::getDefault()->getName() . "\x00" . count(Player::$list) . "\x00" . $this->server->getMaxPlayers() . "\x00" . Utils::writeLShort($this->server->getPort()) . $this->server->getIp() . "\x00"; } $pk->encode(); $this->server->send($pk); diff --git a/src/PocketMine/network/rcon/RCON.php b/src/PocketMine/network/rcon/RCON.php index 63f344f78..881080aa1 100644 --- a/src/PocketMine/network/rcon/RCON.php +++ b/src/PocketMine/network/rcon/RCON.php @@ -26,7 +26,7 @@ namespace PocketMine\Network\RCON; use PocketMine; -use PocketMine\ServerAPI; +use PocketMine\Server; class RCON{ @@ -55,7 +55,7 @@ class RCON{ } @socket_getsockname($this->socket, $addr, $port); console("[INFO] RCON running on $addr:$port"); - ServerAPI::request()->schedule(2, array($this, "check"), array(), true); + Server::getInstance()->schedule(2, array($this, "check"), array(), true); } public function stop(){ @@ -78,7 +78,7 @@ class RCON{ console($this->workers[$n]->response); $this->workers[$n]->notify(); }else{ - $this->workers[$n]->response = ServerAPI::request()->api->console->run($this->workers[$n]->cmd, "rcon"); + $this->workers[$n]->response = Server::getInstance()->api->console->run($this->workers[$n]->cmd, "rcon"); $this->workers[$n]->notify(); } } diff --git a/src/PocketMine/permission/Permissible.php b/src/PocketMine/permission/Permissible.php index ce57d51e6..e5ff56119 100644 --- a/src/PocketMine/permission/Permissible.php +++ b/src/PocketMine/permission/Permissible.php @@ -22,6 +22,7 @@ namespace PocketMine\Permission; use PocketMine; +use PocketMine\Plugin\Plugin; interface Permissible extends ServerOperator{ @@ -43,7 +44,21 @@ interface Permissible extends ServerOperator{ */ public function hasPermission($name); - //TODO: Attachment + /** + * @param Plugin $plugin + * @param string $name + * @param bool $value + * + * @return PermissionAttachment + */ + public function addAttachment(Plugin $plugin, $name = null, $value = null); + + /** + * @param PermissionAttachment $attachment + * + * @return void + */ + public function removeAttachment(PermissionAttachment $attachment); /** diff --git a/src/PocketMine/permission/PermissibleBase.php b/src/PocketMine/permission/PermissibleBase.php new file mode 100644 index 000000000..8afee3338 --- /dev/null +++ b/src/PocketMine/permission/PermissibleBase.php @@ -0,0 +1,220 @@ +opable = $opable; + if($opable instanceof Permissible){ + $this->parent = $opable; + }else{ + $this->parent = $this; + } + } + + /** + * @return bool + */ + public function isOp(){ + if($this->opable === null){ + return false; + }else{ + return $this->opable->isOp(); + } + } + + /** + * @param bool $value + */ + public function setOp($value){ + if($this->opable === null){ + trigger_error("Cannot change ip value as no ServerOperator is set", E_USER_WARNING); + return; + }else{ + $this->opable->setOp($value); + } + } + + /** + * @param Permission|string $name + * + * @return bool + */ + public function isPermissionSet($name){ + return isset($this->permissions[$name instanceof Permission ? $name->getName() : $name]); + } + + /** + * @param Permission|string $name + * + * @return bool + */ + public function hasPermission($name){ + if($name instanceof Permission){ + $name = $name->getName(); + } + + if($this->isPermissionSet($name)){ + return $this->permissions[$name]->getValue(); + } + + if(($perm = Server::getInstance()->getPluginManager()->getPermission($name)) !== null){ + $perm = $perm->getDefault(); + return $perm === Permission::DEFAULT_TRUE or ($this->isOp() and $perm === Permission::DEFAULT_OP) or (!$this->isOp() and $perm === Permission::DEFAULT_NOT_OP); + }else{ + return Permission::$DEFAULT_PERMISSION === Permission::DEFAULT_TRUE or ($this->isOp() and Permission::$DEFAULT_PERMISSION === Permission::DEFAULT_OP) or (!$this->isOp() and Permission::$DEFAULT_PERMISSION === Permission::DEFAULT_NOT_OP); + } + + } + + /** + * //TODO: tick scheduled attachments + * + * @param Plugin $plugin + * @param string $name + * @param bool $value + * + * @return PermissionAttachment + */ + public function addAttachment(Plugin $plugin, $name = null, $value = null){ + if($plugin === null){ + trigger_error("Plugin cannot be null", E_USER_WARNING); + return null; + }elseif(!$plugin->isEnabled()){ + trigger_error("Plugin ".$plugin->getDescription()->getName()." is disabled", E_USER_WARNING); + return null; + } + + $result = new PermissionAttachment($plugin, $this->parent); + $this->attachments[spl_object_hash($result)] = $result; + if($name !== null and $value !== null){ + $result->setPermission($name, $value); + } + + $this->recalculatePermissions(); + + return $result; + } + + /** + * @param PermissionAttachment $attachment + * + * @return void + */ + public function removeAttachment(PermissionAttachment $attachment){ + if($attachment === null){ + trigger_error("Attachment cannot be null", E_USER_WARNING); + return; + } + + if(isset($this->attachments[spl_object_hash($attachment)])){ + unset($this->attachments[spl_object_hash($attachment)]); + if(($ex = $attachment->getRemovalCallback()) !== null){ + $ex->attachmentRemoved($attachment); + } + + $this->recalculatePermissions(); + + } + + } + + public function recalculatePermissions(){ + $this->clearPermissions(); + $defaults = Server::getInstance()->getPluginManager()->getDefaultPermissions($this->isOp()); + Server::getInstance()->getPluginManager()->subscribeToDefaultPerms($this->isOp(), $this->parent); + + foreach($defaults as $perm){ + $name = $perm->getName(); + $this->permissions[$name] = new PermissionAttachmentInfo($this->parent, $name, null, true); + Server::getInstance()->getPluginManager()->subscribeToPermission($name, $this->parent); + $this->calculateChildPermissions($perm->getChildren(), false, null); + } + + foreach($this->attachments as $attachment){ + $this->calculateChildPermissions($attachment->getPermissions(), false, $attachment); + } + } + + public function clearPermissions(){ + foreach(array_keys($this->permissions) as $name){ + Server::getInstance()->getPluginManager()->unsubscribeFromPermission($name, $this->parent); + } + + Server::getInstance()->getPluginManager()->unsubscribeFromDefaultPerms(false, $this->parent); + Server::getInstance()->getPluginManager()->unsubscribeFromDefaultPerms(true, $this->parent); + + $this->permissions = array(); + } + + /** + * @param bool[] $children + * @param bool $invert + * @param PermissionAttachment $attachment + */ + public function calculateChildPermissions(array $children, $invert, PermissionAttachment $attachment){ + foreach(array_keys($children) as $name){ + $perm = Server::getInstance()->getPluginManager()->getPermission($name); + $value = $invert === true ? !$children[$name] : $children[$name]; + $this->permissions[$name] = new PermissionAttachmentInfo($this->parent, $name, $attachment, $value); + Server::getInstance()->getPluginManager()->subscribeToPermission($name, $this->parent); + + if($perm instanceof Permission){ + $this->calculateChildPermissions($perm->getChildren(), !$value, $attachment); + } + } + } + + /** + * @return PermissionAttachmentInfo[] + */ + public function getEffectivePermissions(){ + return $this->permissions; + } +} \ No newline at end of file diff --git a/src/PocketMine/permission/PermissionAttachment.php b/src/PocketMine/permission/PermissionAttachment.php new file mode 100644 index 000000000..eca4bd7c5 --- /dev/null +++ b/src/PocketMine/permission/PermissionAttachment.php @@ -0,0 +1,122 @@ +isEnabled()){ + trigger_error("Plugin ".$plugin->getDescription()->getName()." is disabled", E_USER_WARNING); + return; + } + + $this->permissible = $permissible; + $this->plugin = $plugin; + } + + /** + * @return Plugin + */ + public function getPlugin(){ + return $this->plugin; + } + + /** + * @param PermissionRemovedExecutor $ex + */ + public function setRemovalCallback(PermissionRemovedExecutor $ex){ + $this->removed = $ex; + } + + /** + * @return PermissionRemovedExecutor + */ + public function getRemovalCallback(){ + return $this->removed; + } + + /** + * @return Permissible + */ + public function getPermissible(){ + return $this->permissible; + } + + /** + * @return bool[] + */ + public function getPermissions(){ + return $this->permissions; + } + + /** + * @param string|Permission $name + * @param bool $value + */ + public function setPermission($name, $value){ + $this->permissions[$name instanceof Permission ? $name->getName() : $name] = $value; + $this->permissible->recalculatePermissions(); + } + + /** + * @param string|Permission $name + */ + public function unsetPermission($name){ + unset($this->permissions[$name instanceof Permission ? $name->getName() : $name]); + } + + /** + * @return void + */ + public function remove(){ + $this->permissible->removeAttachment($this); + } +} \ No newline at end of file diff --git a/src/PocketMine/permission/PermissionAttachmentInfo.php b/src/PocketMine/permission/PermissionAttachmentInfo.php new file mode 100644 index 000000000..3f4945e70 --- /dev/null +++ b/src/PocketMine/permission/PermissionAttachmentInfo.php @@ -0,0 +1,95 @@ +permissible = $permissible; + $this->permission = $permission; + $this->attachment = $attachment; + $this->value = $value; + } + + /** + * @return Permissible + */ + public function getPermissible(){ + return $this->permissible; + } + + /** + * @return string + */ + public function getPermission(){ + return $this->permission; + } + + /** + * @return PermissionAttachment + */ + public function getAttachment(){ + return $this->attachment; + } + + /** + * @return bool + */ + public function getValue(){ + return $this->value; + } +} \ No newline at end of file diff --git a/src/PocketMine/permission/PermissionRemovedExecutor.php b/src/PocketMine/permission/PermissionRemovedExecutor.php new file mode 100644 index 000000000..92211cb44 --- /dev/null +++ b/src/PocketMine/permission/PermissionRemovedExecutor.php @@ -0,0 +1,34 @@ +plugins[$name])){ + return $this->plugins[$name]; } return null; @@ -88,7 +100,7 @@ abstract class PluginManager{ * * @return boolean */ - public static function registerInterface($loader){ + public function registerInterface($loader){ if(is_object($loader) and !($loader instanceof PluginLoader)){ return false; }elseif(is_string($loader)){ @@ -99,7 +111,7 @@ abstract class PluginManager{ } } - static::$fileAssociations[spl_object_hash($loader)] = array($loader, $loader->getPluginFilters()); + $this->fileAssociations[spl_object_hash($loader)] = array($loader, $loader->getPluginFilters()); return true; } @@ -107,8 +119,8 @@ abstract class PluginManager{ /** * @return Plugin[] */ - public static function getPlugins(){ - return static::$plugins; + public function getPlugins(){ + return $this->plugins; } /** @@ -116,9 +128,9 @@ abstract class PluginManager{ * * @return Plugin */ - public static function loadPlugin($path){ - foreach(static::$fileAssociations as $loader){ - if(preg_match($loader[1], basename($file)) > 0){ + public function loadPlugin($path){ + foreach($this->fileAssociations as $loader){ + if(preg_match($loader[1], basename($path)) > 0){ $description = $loader[0]->getPluginDescription($path); if($description instanceof PluginDescription){ return $loader[0]->loadPlugin($path); @@ -134,14 +146,14 @@ abstract class PluginManager{ * * @return Plugin[] */ - public static function loadPlugins($directory){ + public function loadPlugins($directory){ if(is_dir($directory)){ $plugins = array(); $loadedPlugins = array(); $dependencies = array(); $softDependencies = array(); foreach(new \IteratorIterator(new \DirectoryIterator($directory)) as $file){ - foreach(static::$fileAssociations as $loader){ + foreach($this->fileAssociations as $loader){ if(preg_match($loader[1], basename($file)) > 0){ $description = $loader[0]->getPluginDescription($file); if($description instanceof PluginDescription){ @@ -236,7 +248,7 @@ abstract class PluginManager{ if(!isset($dependencies[$name]) and !isset($softDependencies[$name])){ unset($plugins[$name]); $missingDependency = false; - if($plugin = static::loadPlugin($file) and $plugin instanceof Plugin){ + if($plugin = $this->loadPlugin($file) and $plugin instanceof Plugin){ $loadedPlugins[$name] = $plugin; }else{ console("[SEVERE] Could not load plugin '" . $name . "'"); @@ -250,7 +262,7 @@ abstract class PluginManager{ unset($softDependencies[$name]); unset($plugins[$name]); $missingDependency = false; - if($plugin = static::loadPlugin($file) and $plugin instanceof Plugin){ + if($plugin = $this->loadPlugin($file) and $plugin instanceof Plugin){ $loadedPlugins[$name] = $plugin; }else{ console("[SEVERE] Could not load plugin '" . $name . "'"); @@ -279,9 +291,9 @@ abstract class PluginManager{ * * @return null|Permission */ - public static function getPermission($name){ - if(isset(static::$permissions[$name])){ - return static::$permissions[$name]; + public function getPermission($name){ + if(isset($this->permissions[$name])){ + return $this->permissions[$name]; } return null; @@ -292,10 +304,10 @@ abstract class PluginManager{ * * @return bool */ - public static function addPermission(Permission $permission){ - if(!isset(static::$permissions[$permission->getName()])){ - static::$permissions[$permission->getName()] = $permission; - static::calculatePermissionDefault($permission); + public function addPermission(Permission $permission){ + if(!isset($this->permissions[$permission->getName()])){ + $this->permissions[$permission->getName()] = $permission; + $this->calculatePermissionDefault($permission); return true; } @@ -308,9 +320,9 @@ abstract class PluginManager{ */ public function removePermission($permission){ if($permission instanceof Permission){ - unset(static::$permissions[$permission->getName()]); + unset($this->permissions[$permission->getName()]); }else{ - unset(static::$permissions[$permission]); + unset($this->permissions[$permission]); } } @@ -319,45 +331,45 @@ abstract class PluginManager{ * * @return Permission[] */ - public static function getDefaultPermissions($op){ + public function getDefaultPermissions($op){ if($op === true){ - return static::$defaultPermsOp; + return $this->defaultPermsOp; }else{ - return static::$defaultPerms; + return $this->defaultPerms; } } /** * @param Permission $permission */ - public static function recalculatePermissionDefaults(Permission $permission){ - if(isset(static::$permissions[$permission->getName()])){ - unset(static::$defaultPermsOp[$permission->getName()]); - unset(static::$defaultPerms[$permission->getName()]); - static::calculatePermissionDefault($permission); + public function recalculatePermissionDefaults(Permission $permission){ + if(isset($this->permissions[$permission->getName()])){ + unset($this->defaultPermsOp[$permission->getName()]); + unset($this->defaultPerms[$permission->getName()]); + $this->calculatePermissionDefault($permission); } } /** * @param Permission $permission */ - private static function calculatePermissionDefault(Permission $permission){ + private function calculatePermissionDefault(Permission $permission){ if($permission->getDefault() === Permission::DEFAULT_OP or $permission->getDefault() === Permission::DEFAULT_TRUE){ - static::$defaultPermsOp[$permission->getName()] = $permission; - static::dirtyPermissibles(true); + $this->defaultPermsOp[$permission->getName()] = $permission; + $this->dirtyPermissibles(true); } if($permission->getDefault() === Permission::DEFAULT_NOT_OP or $permission->getDefault() === Permission::DEFAULT_TRUE){ - static::$defaultPerms[$permission->getName()] = $permission; - static::dirtyPermissibles(false); + $this->defaultPerms[$permission->getName()] = $permission; + $this->dirtyPermissibles(false); } } /** * @param boolean $op */ - private static function dirtyPermissibles($op){ - foreach(static::getDefaultPermSubscriptions($op) as $p){ + private function dirtyPermissibles($op){ + foreach($this->getDefaultPermSubscriptions($op) as $p){ $p->recalculatePermissions(); } } @@ -366,21 +378,21 @@ abstract class PluginManager{ * @param string $permission * @param Permissible $permissible */ - public static function subscribeToPermission($permission, Permissible $permissible){ - if(!isset(static::$permSubs[$permission])){ + public function subscribeToPermission($permission, Permissible $permissible){ + if(!isset($this->permSubs[$permission])){ //TODO: Use WeakRef - static::$permSubs[$permission] = array(); + $this->permSubs[$permission] = array(); } - static::$permSubs[$permission][spl_object_hash($permissible)] = $permissible; + $this->permSubs[$permission][spl_object_hash($permissible)] = $permissible; } /** * @param string $permission * @param Permissible $permissible */ - public static function unsubscribeFromPermission($permission, Permissible $permissible){ - if(isset(static::$permSubs[$permission])){ - unset(static::$permSubs[$permission][spl_object_hash($permissible)]); + public function unsubscribeFromPermission($permission, Permissible $permissible){ + if(isset($this->permSubs[$permission])){ + unset($this->permSubs[$permission][spl_object_hash($permissible)]); } } @@ -389,9 +401,9 @@ abstract class PluginManager{ * * @return Permissible[] */ - public static function getPermissionSubscriptions($permission){ - if(isset(static::$permSubs[$permission])){ - return static::$permSubs[$permission]; + public function getPermissionSubscriptions($permission){ + if(isset($this->permSubs[$permission])){ + return $this->permSubs[$permission]; } return array(); @@ -401,11 +413,11 @@ abstract class PluginManager{ * @param boolean $op * @param Permissible $permissible */ - public static function subscribeToDefaultPerms($op, Permissible $permissible){ + public function subscribeToDefaultPerms($op, Permissible $permissible){ if($op === true){ - static::$defSubsOp[spl_object_hash($permissible)] = $permissible; + $this->defSubsOp[spl_object_hash($permissible)] = $permissible; }else{ - static::$defSubs[spl_object_hash($permissible)] = $permissible; + $this->defSubs[spl_object_hash($permissible)] = $permissible; } } @@ -413,11 +425,11 @@ abstract class PluginManager{ * @param boolean $op * @param Permissible $permissible */ - public static function unsubscribeFromDefaultPerms($op, Permissible $permissible){ + public function unsubscribeFromDefaultPerms($op, Permissible $permissible){ if($op === true){ - unset(static::$defSubsOp[spl_object_hash($permissible)]); + unset($this->defSubsOp[spl_object_hash($permissible)]); }else{ - unset(static::$defSubs[spl_object_hash($permissible)]); + unset($this->defSubs[spl_object_hash($permissible)]); } } @@ -426,19 +438,19 @@ abstract class PluginManager{ * * @return Permissible[] */ - public static function getDefaultPermSubscriptions($op){ + public function getDefaultPermSubscriptions($op){ if($op === true){ - return static::$defSubsOp; + return $this->defSubsOp; }else{ - return static::$defSubs; + return $this->defSubs; } } /** * @return Permission[] */ - public static function getPermissions(){ - return static::$permissions; + public function getPermissions(){ + return $this->permissions; } diff --git a/src/PocketMine/pmf/Plugin.php b/src/PocketMine/pmf/Plugin.php deleted file mode 100644 index 466c56944..000000000 --- a/src/PocketMine/pmf/Plugin.php +++ /dev/null @@ -1,85 +0,0 @@ -load($file); - $this->parseInfo(); - $this->parsePlugin(); - } - - public function getPluginInfo(){ - return $this->pluginData; - } - - protected function parsePlugin(){ - if($this->getType() !== 0x01){ - return false; - } - $this->seek(5); - $this->pluginData["fversion"] = ord($this->read(1)); - if($this->pluginData["fversion"] > self::VERSION){ - return false; - } - $this->pluginData["name"] = $this->read(Utils::readShort($this->read(2), false)); - $this->pluginData["version"] = $this->read(Utils::readShort($this->read(2), false)); - $this->pluginData["author"] = $this->read(Utils::readShort($this->read(2), false)); - if($this->pluginData["fversion"] >= 0x01){ - $this->pluginData["apiversion"] = $this->read(Utils::readShort($this->read(2), false)); - }else{ - $this->pluginData["apiversion"] = Utils::readShort($this->read(2), false); - } - $this->pluginData["class"] = $this->read(Utils::readShort($this->read(2), false)); - $this->pluginData["identifier"] = $this->read(Utils::readShort($this->read(2), false)); //Will be used to check for updates - if($this->pluginData["fversion"] >= 0x02){ - $data = explode(";", gzinflate($this->read(Utils::readInt($this->read(4))))); - $this->pluginData["extra"] = array(); - foreach($data as $v){ - $v = trim($v); - if($v != ""){ - $v = base64_decode($v); - $kl = strpos($v, ":"); - $this->pluginData["extra"][substr($v, 0, $kl)] = substr($v, $kl + 1); - } - } - - }else{ - $this->pluginData["extra"] = gzinflate($this->read(Utils::readShort($this->read(2), false))); - } - $this->pluginData["code"] = ""; - while(!feof($this->fp)){ - $this->pluginData["code"] .= $this->read(4096); - } - $this->pluginData["code"] = gzinflate($this->pluginData["code"]); - - return true; - } - -} \ No newline at end of file diff --git a/src/PocketMine/recipes/Crafting.php b/src/PocketMine/recipes/Crafting.php index 4a6bc9d7c..ff41214b2 100644 --- a/src/PocketMine/recipes/Crafting.php +++ b/src/PocketMine/recipes/Crafting.php @@ -26,7 +26,7 @@ namespace PocketMine\Recipes; use PocketMine; use PocketMine\Item\Item; -use PocketMine\ServerAPI; +use PocketMine\Server; abstract class Crafting{ @@ -263,7 +263,6 @@ abstract class Crafting{ foreach(self::$small as $recipe){ $recipe = self::parseRecipe($recipe); self::$recipes[$id] = $recipe; - self::$lookupTable[0][$recipe[2]]; if(!isset(self::$lookupTable[0][$recipe[2]])){ self::$lookupTable[0][$recipe[2]] = array(); } diff --git a/src/PocketMine/event/scheduler/ServerAsyncTask.php b/src/PocketMine/scheduler/ServerAsyncTask.php similarity index 100% rename from src/PocketMine/event/scheduler/ServerAsyncTask.php rename to src/PocketMine/scheduler/ServerAsyncTask.php diff --git a/src/PocketMine/event/scheduler/ServerCallable.php b/src/PocketMine/scheduler/ServerCallable.php similarity index 100% rename from src/PocketMine/event/scheduler/ServerCallable.php rename to src/PocketMine/scheduler/ServerCallable.php diff --git a/src/PocketMine/event/scheduler/ServerFuture.php b/src/PocketMine/scheduler/ServerFuture.php similarity index 100% rename from src/PocketMine/event/scheduler/ServerFuture.php rename to src/PocketMine/scheduler/ServerFuture.php diff --git a/src/PocketMine/event/scheduler/ServerRunnable.php b/src/PocketMine/scheduler/ServerRunnable.php similarity index 100% rename from src/PocketMine/event/scheduler/ServerRunnable.php rename to src/PocketMine/scheduler/ServerRunnable.php diff --git a/src/PocketMine/event/scheduler/ServerScheduler.php b/src/PocketMine/scheduler/ServerScheduler.php similarity index 100% rename from src/PocketMine/event/scheduler/ServerScheduler.php rename to src/PocketMine/scheduler/ServerScheduler.php diff --git a/src/PocketMine/event/scheduler/ServerTask.php b/src/PocketMine/scheduler/ServerTask.php similarity index 95% rename from src/PocketMine/event/scheduler/ServerTask.php rename to src/PocketMine/scheduler/ServerTask.php index ab9b98c8a..6a658524d 100644 --- a/src/PocketMine/event/scheduler/ServerTask.php +++ b/src/PocketMine/scheduler/ServerTask.php @@ -56,7 +56,7 @@ class ServerTask extends \Threaded{ */ private $id; - public function __construct(Plugin $plugin, \Threaded $task, $id = -1, $period = -1){ + public function __construct(Plugin $plugin = null, \Threaded $task = null, $id = -1, $period = -1){ $this->plugin = $plugin; $this->task = $task; $this->id = $id; diff --git a/src/PocketMine/event/scheduler/ServerWorker.php b/src/PocketMine/scheduler/ServerWorker.php similarity index 100% rename from src/PocketMine/event/scheduler/ServerWorker.php rename to src/PocketMine/scheduler/ServerWorker.php diff --git a/src/PocketMine/scheduler/TickScheduler.php b/src/PocketMine/scheduler/TickScheduler.php new file mode 100644 index 000000000..5795081d4 --- /dev/null +++ b/src/PocketMine/scheduler/TickScheduler.php @@ -0,0 +1,78 @@ +ticksPerSecond = (int) $ticksPerSecond; + $this->sleepTime = (int) (1000000 / $this->ticksPerSecond); + $this->tickMeasure = $this->sleepTime; + $this->start(PTHREADS_INHERIT_ALL & ~PTHREADS_INHERIT_CLASSES); + } + + /** + * Returns true if clear to run tick + * + * @return bool + */ + public function hasTick(){ + return $this->synchronized(function(){ + $hasTick = $this->hasTick; + $this->hasTick = false; + return $hasTick === true; + }); + } + + public function doTick(){ + $this->notify(); + } + + /** + * @return float + */ + public function getTPS(){ + return $this->synchronized(function(){ + return round(($this->sleepTime / $this->tickMeasure) * $this->ticksPerSecond, 2); + }); + } + + public function run(){ + $tickTime = microtime(true); + $this->hasTick = true; + while(true){ + $this->synchronized(function(){ + $this->hasTick = true; + $this->wait(); + $this->hasTick = false; + }); + + $this->tickMeasure = (int) ((($time = microtime(true)) - $tickTime) * 1000000); + $tickTime = $time; + usleep($this->sleepTime - 100); //Remove a few ms for processing + } + } +} \ No newline at end of file diff --git a/src/PocketMine/tile/Tile.php b/src/PocketMine/tile/Tile.php index 158a0c8bc..bbb7656ed 100644 --- a/src/PocketMine/tile/Tile.php +++ b/src/PocketMine/tile/Tile.php @@ -29,7 +29,7 @@ use PocketMine\Level\Level; use PocketMine\Level\Position; use PocketMine\NBT\Tag\Compound; use PocketMine\PMF\LevelFormat; -use PocketMine\ServerAPI; +use PocketMine\Server; use PocketMine; abstract class Tile extends Position{ @@ -38,7 +38,15 @@ abstract class Tile extends Position{ const FURNACE = "Furnace"; public static $tileCount = 1; + + /** + * @var Tile[] + */ public static $list = array(); + + /** + * @var Tile[] + */ public static $needUpdate = array(); public $chunkIndex; @@ -68,7 +76,7 @@ abstract class Tile extends Position{ public function __construct(Level $level, Compound $nbt){ - $this->server = ServerAPI::request(); + $this->server = Server::getInstance(); $this->level = $level; $this->namedtag = $nbt; $this->closed = false; diff --git a/src/stubs/pthreads.php b/src/stubs/pthreads.php index eff78e096..ce6386bff 100644 --- a/src/stubs/pthreads.php +++ b/src/stubs/pthreads.php @@ -319,7 +319,7 @@ class Thread extends Threaded * @link http://www.php.net/manual/en/thread.start.php * @return bool A boolean indication of success */ - public static function globally(Callable $block){} + public static function globally(Callable $block, $args = null){} } /** diff --git a/src/tests/ServerSuiteTest.php b/src/tests/ServerSuiteTest.php index 20c2e578f..b0307609c 100644 --- a/src/tests/ServerSuiteTest.php +++ b/src/tests/ServerSuiteTest.php @@ -58,7 +58,7 @@ class ServerSuiteTest{ public function hook(){ testCase("event fired", true, true); - $server = \PocketMine\ServerAPI::request(); + $server = \PocketMine\Server::getInstance(); testCase("defaultgamemode", $server->getGamemode(), "survival");