This commit is contained in:
Michael Yoo 2014-07-13 22:04:41 +09:30
commit 6fbfa1836f
28 changed files with 909 additions and 116 deletions

View File

@ -5,7 +5,7 @@
Before contributing to PocketMine-MP, please read this. Also, take a look if your contribution fits the PocketMine-MP goals below. Before contributing to PocketMine-MP, please read this. Also, take a look if your contribution fits the PocketMine-MP goals below.
## I've a question ## I have a question
* For questions, please refer to the _#pocketmine_ or _#mcpedevs_ IRC channel on Freenode. There is a [WebIRC](http://webchat.freenode.net?channels=pocketmine,mcpedevs&uio=d4) if you want. * For questions, please refer to the _#pocketmine_ or _#mcpedevs_ IRC channel on Freenode. There is a [WebIRC](http://webchat.freenode.net?channels=pocketmine,mcpedevs&uio=d4) if you want.
* You can ask directly to _[@PocketMine](https://twitter.com/PocketMine)_ in Twitter, but don't expect an inmediate reply. * You can ask directly to _[@PocketMine](https://twitter.com/PocketMine)_ in Twitter, but don't expect an inmediate reply.

View File

@ -47,6 +47,7 @@ use pocketmine\event\player\PlayerQuitEvent;
use pocketmine\event\player\PlayerRespawnEvent; use pocketmine\event\player\PlayerRespawnEvent;
use pocketmine\event\server\DataPacketReceiveEvent; use pocketmine\event\server\DataPacketReceiveEvent;
use pocketmine\event\server\DataPacketSendEvent; use pocketmine\event\server\DataPacketSendEvent;
use pocketmine\event\Timings;
use pocketmine\inventory\BaseTransaction; use pocketmine\inventory\BaseTransaction;
use pocketmine\inventory\BigShapelessRecipe; use pocketmine\inventory\BigShapelessRecipe;
use pocketmine\inventory\CraftingTransactionGroup; use pocketmine\inventory\CraftingTransactionGroup;
@ -504,9 +505,10 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
$this->dataPacket($pk); $this->dataPacket($pk);
$this->getLevel()->freeChunk($x, $z, $this); $this->getLevel()->freeChunk($x, $z, $this);
unset($this->usedChunks[$index]); unset($this->usedChunks[$index]);
$this->orderChunks();
} }
unset($this->loadQueue[$index]); unset($this->loadQueue[$index]);
$this->orderChunks();
} }
/** /**
@ -1845,7 +1847,9 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
break; break;
} }
if(substr($ev->getMessage(), 0, 1) === "/"){ //Command if(substr($ev->getMessage(), 0, 1) === "/"){ //Command
Timings::$playerCommandTimer->startTiming();
$this->server->dispatchCommand($ev->getPlayer(), substr($ev->getMessage(), 1)); $this->server->dispatchCommand($ev->getPlayer(), substr($ev->getMessage(), 1));
Timings::$playerCommandTimer->stopTiming();
}else{ }else{
$this->server->getPluginManager()->callEvent($ev = new PlayerChatEvent($this, $ev->getMessage())); $this->server->getPluginManager()->callEvent($ev = new PlayerChatEvent($this, $ev->getMessage()));
if(!$ev->isCancelled()){ if(!$ev->isCancelled()){

View File

@ -74,7 +74,7 @@ namespace pocketmine {
use raklib\RakLib; use raklib\RakLib;
const VERSION = "Alpha_1.4dev"; const VERSION = "Alpha_1.4dev";
const API_VERSION = "1.0.0"; const API_VERSION = "1.1.0";
const CODENAME = "絶好(Zekkou)ケーキ(Cake)"; const CODENAME = "絶好(Zekkou)ケーキ(Cake)";
const MINECRAFT_VERSION = "v0.9.0 alpha"; const MINECRAFT_VERSION = "v0.9.0 alpha";
const PHP_VERSION = "5.5"; const PHP_VERSION = "5.5";
@ -135,7 +135,7 @@ namespace pocketmine {
} }
} }
gc_enable(); gc_disable();
error_reporting(E_ALL | E_STRICT); error_reporting(E_ALL | E_STRICT);
ini_set("allow_url_fopen", 1); ini_set("allow_url_fopen", 1);
ini_set("display_errors", 1); ini_set("display_errors", 1);

View File

@ -36,6 +36,8 @@ use pocketmine\event\HandlerList;
use pocketmine\event\level\LevelInitEvent; use pocketmine\event\level\LevelInitEvent;
use pocketmine\event\level\LevelLoadEvent; use pocketmine\event\level\LevelLoadEvent;
use pocketmine\event\server\ServerCommandEvent; use pocketmine\event\server\ServerCommandEvent;
use pocketmine\event\Timings;
use pocketmine\event\TimingsHandler;
use pocketmine\inventory\CraftingManager; use pocketmine\inventory\CraftingManager;
use pocketmine\inventory\InventoryType; use pocketmine\inventory\InventoryType;
use pocketmine\inventory\Recipe; use pocketmine\inventory\Recipe;
@ -71,6 +73,7 @@ use pocketmine\plugin\Plugin;
use pocketmine\plugin\PluginLoadOrder; use pocketmine\plugin\PluginLoadOrder;
use pocketmine\plugin\PluginManager; use pocketmine\plugin\PluginManager;
use pocketmine\scheduler\CallbackTask; use pocketmine\scheduler\CallbackTask;
use pocketmine\scheduler\PHPGarbageCollectionTask;
use pocketmine\scheduler\SendUsageTask; use pocketmine\scheduler\SendUsageTask;
use pocketmine\scheduler\ServerScheduler; use pocketmine\scheduler\ServerScheduler;
use pocketmine\tile\Tile; use pocketmine\tile\Tile;
@ -1354,7 +1357,7 @@ class Server{
$this->filePath = $filePath; $this->filePath = $filePath;
$this->dataPath = $dataPath; $this->dataPath = $dataPath;
$this->pluginPath = $pluginPath; $this->pluginPath = $pluginPath;
@mkdir($this->dataPath . "worlds/", 0777); @mkdir($this->dataPath . "worlds/", 0777, true);
@mkdir($this->dataPath . "players/", 0777); @mkdir($this->dataPath . "players/", 0777);
@mkdir($this->pluginPath, 0777); @mkdir($this->pluginPath, 0777);
@ -1474,8 +1477,12 @@ class Server{
Item::init(); Item::init();
$this->craftingManager = new CraftingManager(); $this->craftingManager = new CraftingManager();
PluginManager::$pluginParentTimer = new TimingsHandler("** Plugins");
Timings::init();
$this->pluginManager = new PluginManager($this, $this->commandMap); $this->pluginManager = new PluginManager($this, $this->commandMap);
$this->pluginManager->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this->consoleSender); $this->pluginManager->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this->consoleSender);
$this->pluginManager->setUseTimings($this->getProperty("settings.enable-profiling", false));
$this->pluginManager->registerInterface("pocketmine\\plugin\\PharPluginLoader"); $this->pluginManager->registerInterface("pocketmine\\plugin\\PharPluginLoader");
$this->pluginManager->loadPlugins($this->pluginPath); $this->pluginManager->loadPlugins($this->pluginPath);
@ -1516,9 +1523,11 @@ class Server{
} }
if($this->getProperty("chunk-gc.period-in-ticks", 600) > 0){ if($this->getProperty("chunk-gc.period-in-ticks", 600) > 0){
$this->scheduler->scheduleDelayedRepeatingTask(new CallbackTask(array($this, "doLevelGC")), $this->getProperty("chunk-gc.period-in-ticks", 600), $this->getProperty("chunk-gc.period-in-ticks", 600)); $this->scheduler->scheduleDelayedRepeatingTask(new CallbackTask([$this, "doLevelGC"]), $this->getProperty("chunk-gc.period-in-ticks", 600), $this->getProperty("chunk-gc.period-in-ticks", 600));
} }
$this->scheduler->scheduleRepeatingTask(new PHPGarbageCollectionTask(), 100);
$this->enablePlugins(PluginLoadOrder::POSTWORLD); $this->enablePlugins(PluginLoadOrder::POSTWORLD);
} }
@ -1596,10 +1605,15 @@ class Server{
} }
public function checkConsole(){ public function checkConsole(){
Timings::$serverCommandTimer->startTiming();
if(($line = $this->console->getLine()) !== null){ if(($line = $this->console->getLine()) !== null){
$this->pluginManager->callEvent($ev = new ServerCommandEvent($this->consoleSender, $line)); $this->pluginManager->callEvent($ev = new ServerCommandEvent($this->consoleSender, $line));
if($ev->isCancelled()){
return;
}
$this->dispatchCommand($this->consoleSender, $ev->getCommand()); $this->dispatchCommand($this->consoleSender, $ev->getCommand());
} }
Timings::$serverCommandTimer->stopTiming();
} }
/** /**
@ -1620,7 +1634,6 @@ class Server{
}else{ }else{
$sender->sendMessage("Unknown command. Type \"help\" for help."); $sender->sendMessage("Unknown command. Type \"help\" for help.");
} }
return false; return false;
} }
@ -1664,6 +1677,7 @@ class Server{
$this->pluginManager->loadPlugins($this->pluginPath); $this->pluginManager->loadPlugins($this->pluginPath);
$this->enablePlugins(PluginLoadOrder::STARTUP); $this->enablePlugins(PluginLoadOrder::STARTUP);
$this->enablePlugins(PluginLoadOrder::POSTWORLD); $this->enablePlugins(PluginLoadOrder::POSTWORLD);
TimingsHandler::reload();
} }
/** /**
@ -1743,35 +1757,10 @@ class Server{
$this->logger->info("Done (" . round(microtime(true) - \pocketmine\START_TIME, 3) . 's)! For help, type "help" or "?"'); $this->logger->info("Done (" . round(microtime(true) - \pocketmine\START_TIME, 3) . 's)! For help, type "help" or "?"');
//if(Utils::getOS() === "win"){ //Workaround less usleep() waste $this->tickProcessor();
// $this->tickProcessorWindows();
//}else{
$this->tickProcessor();
//}
$this->forceShutdown(); $this->forceShutdown();
} }
/*private function tickProcessorWindows(){
$lastLoop = 0;
while($this->isRunning){
foreach($this->interfaces as $interface){
if($interface->process()){
$lastLoop = 0;
}
}
$this->generationManager->handlePackets();
if(($ticks = $this->tick()) !== true){
++$lastLoop;
if($lastLoop > 8){
usleep(1000);
}
}else{
$lastLoop = 0;
}
}
}*/
public function checkTicks(){ public function checkTicks(){
if($this->getTicksPerSecond() < 12){ if($this->getTicksPerSecond() < 12){
$this->logger->warning("Can't keep up! Is the server overloaded?"); $this->logger->warning("Can't keep up! Is the server overloaded?");
@ -1875,13 +1864,17 @@ class Server{
private function tickProcessor(){ private function tickProcessor(){
$lastLoop = 0; $lastLoop = 0;
$connectionTimer = Timings::$connectionTimer;
while($this->isRunning){ while($this->isRunning){
$connectionTimer->startTiming();
foreach($this->interfaces as $interface){ foreach($this->interfaces as $interface){
if($interface->process()){ if($interface->process()){
$lastLoop = 0; $lastLoop = 0;
} }
} }
$connectionTimer->stopTiming();
$this->generationManager->handlePackets(); $this->generationManager->handlePackets();
++$lastLoop; ++$lastLoop;
@ -1903,22 +1896,29 @@ class Server{
} }
private function checkTickUpdates($currentTick){ private function checkTickUpdates($currentTick){
//TODO: move this to each Level
//Update entities that need update //Update entities that need update
if(count(Entity::$needUpdate) > 0){ if(count(Entity::$needUpdate) > 0){
Timings::$tickEntityTimer->startTiming();
foreach(Entity::$needUpdate as $id => $entity){ foreach(Entity::$needUpdate as $id => $entity){
if($entity->onUpdate() === false){ if($entity->onUpdate() === false){
unset(Entity::$needUpdate[$id]); unset(Entity::$needUpdate[$id]);
} }
} }
Timings::$tickEntityTimer->stopTiming();
} }
//Update tiles that need update //Update tiles that need update
if(count(Tile::$needUpdate) > 0){ if(count(Tile::$needUpdate) > 0){
Timings::$tickTileEntityTimer->startTiming();
foreach(Tile::$needUpdate as $id => $tile){ foreach(Tile::$needUpdate as $id => $tile){
if($tile->onUpdate() === false){ if($tile->onUpdate() === false){
unset(Tile::$needUpdate[$id]); unset(Tile::$needUpdate[$id]);
} }
} }
Timings::$tickTileEntityTimer->stopTiming();
} }
//TODO: Add level blocks //TODO: Add level blocks
@ -1931,13 +1931,15 @@ class Server{
public function doAutoSave(){ public function doAutoSave(){
/*foreach($this->getOnlinePlayers() as $player){ Timings::$worldSaveTimer->startTiming();
foreach($this->getOnlinePlayers() as $player){
$player->save(); $player->save();
}*/ }
foreach($this->getLevels() as $level){ foreach($this->getLevels() as $level){
$level->save(); $level->save(false);
} }
Timings::$worldSaveTimer->stopTiming();
} }
public function doLevelGC(){ public function doLevelGC(){
@ -1995,11 +1997,15 @@ class Server{
return false; return false;
} }
Timings::$serverTickTimer->startTiming();
$this->inTick = true; //Fix race conditions $this->inTick = true; //Fix race conditions
++$this->tickCounter; ++$this->tickCounter;
$this->checkConsole(); $this->checkConsole();
Timings::$schedulerTimer->startTiming();
$this->scheduler->mainThreadHeartbeat($this->tickCounter); $this->scheduler->mainThreadHeartbeat($this->tickCounter);
Timings::$schedulerTimer->stopTiming();
$this->checkTickUpdates($this->tickCounter); $this->checkTickUpdates($this->tickCounter);
if(($this->tickCounter & 0b1111) === 0){ if(($this->tickCounter & 0b1111) === 0){
@ -2009,11 +2015,15 @@ class Server{
} }
} }
TimingsHandler::tick();
$this->tickMeasure = (($time = microtime(true)) - $this->tickTime); $this->tickMeasure = (($time = microtime(true)) - $this->tickTime);
$this->tickTime = $time; $this->tickTime = $time;
$this->nextTick = 0.05 * (0.05 / max(0.05, $this->tickMeasure)) + $time; $this->nextTick = 0.05 * (0.05 / max(0.05, $this->tickMeasure)) + $time;
$this->inTick = false; $this->inTick = false;
Timings::$serverTickTimer->stopTiming();
return true; return true;
} }

View File

@ -24,6 +24,7 @@
*/ */
namespace pocketmine\command; namespace pocketmine\command;
use pocketmine\event\TimingsHandler;
use pocketmine\Server; use pocketmine\Server;
use pocketmine\utils\TextFormat; use pocketmine\utils\TextFormat;
@ -62,6 +63,9 @@ abstract class Command{
/** @var string */ /** @var string */
private $permissionMessage = null; private $permissionMessage = null;
/** @var TimingsHandler */
public $timings;
/** /**
* @param string $name * @param string $name
* @param string $description * @param string $description
@ -76,6 +80,7 @@ abstract class Command{
$this->usageMessage = $usageMessage === null ? "/" . $name : $usageMessage; $this->usageMessage = $usageMessage === null ? "/" . $name : $usageMessage;
$this->aliases = $aliases; $this->aliases = $aliases;
$this->activeAliases = (array) $aliases; $this->activeAliases = (array) $aliases;
$this->timings = new TimingsHandler("** Command: ". $name);
} }
/** /**
@ -156,8 +161,8 @@ abstract class Command{
public function setLabel($name){ public function setLabel($name){
$this->nextLabel = $name; $this->nextLabel = $name;
if(!$this->isRegistered()){ if(!$this->isRegistered()){
$this->timings = new TimingsHandler("** Command: ". $name);
$this->label = $name; $this->label = $name;
return true; return true;
} }

View File

@ -51,6 +51,7 @@ use pocketmine\command\defaults\StopCommand;
use pocketmine\command\defaults\TeleportCommand; use pocketmine\command\defaults\TeleportCommand;
use pocketmine\command\defaults\TellCommand; use pocketmine\command\defaults\TellCommand;
use pocketmine\command\defaults\TimeCommand; use pocketmine\command\defaults\TimeCommand;
use pocketmine\command\defaults\TimingsCommand;
use pocketmine\command\defaults\VanillaCommand; use pocketmine\command\defaults\VanillaCommand;
use pocketmine\command\defaults\VersionCommand; use pocketmine\command\defaults\VersionCommand;
use pocketmine\command\defaults\WhitelistCommand; use pocketmine\command\defaults\WhitelistCommand;
@ -102,6 +103,7 @@ class SimpleCommandMap implements CommandMap{
$this->register("pocketmine", new SetWorldSpawnCommand("setworldspawn")); $this->register("pocketmine", new SetWorldSpawnCommand("setworldspawn"));
$this->register("pocketmine", new TeleportCommand("tp")); $this->register("pocketmine", new TeleportCommand("tp"));
$this->register("pocketmine", new TimeCommand("time")); $this->register("pocketmine", new TimeCommand("time"));
$this->register("pocketmine", new TimingsCommand("timings"));
$this->register("pocketmine", new ReloadCommand("reload")); $this->register("pocketmine", new ReloadCommand("reload"));
if($this->server->getProperty("debug.commands", false) === true){ if($this->server->getProperty("debug.commands", false) === true){
@ -175,7 +177,9 @@ class SimpleCommandMap implements CommandMap{
return false; return false;
} }
$target->timings->startTiming();
$target->execute($sender, $sentCommandLabel, $args); $target->execute($sender, $sentCommandLabel, $args);
$target->timings->stopTiming();
return true; return true;
} }

View File

@ -0,0 +1,141 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
namespace pocketmine\command\defaults;
use pocketmine\command\CommandSender;
use pocketmine\event\HandlerList;
use pocketmine\event\TimingsHandler;
use pocketmine\Player;
use pocketmine\plugin\TimedRegisteredListener;
use pocketmine\utils\TextFormat;
use pocketmine\utils\Utils;
class TimingsCommand extends VanillaCommand{
public static $timingStart = 0;
public function __construct($name){
parent::__construct(
$name,
"Records timings to see performance of the server.",
"/timings <reset|report|on|off|paste>"
);
$this->setPermission("pocketmine.command.timings");
}
public function execute(CommandSender $sender, $currentAlias, array $args){
if(!$this->testPermission($sender)){
return true;
}
if(count($args) !== 1){
$sender->sendMessage(TextFormat::RED . "Usage: " . $this->usageMessage);
return true;
}
$mode = strtolower($args[0]);
if($mode === "on"){
$sender->getServer()->getPluginManager()->setUseTimings(true);
TimingsHandler::reload();
$sender->sendMessage("Enabled Timings & Reset");
return true;
}elseif($mode === "off"){
$sender->getServer()->getPluginManager()->setUseTimings(false);
$sender->sendMessage("Disabled Timings");
}
if(!$sender->getServer()->getPluginManager()->useTimings()){
$sender->sendMessage("Please enable timings by typing /timings on");
return true;
}
$paste = $mode === "paste";
if($mode === "reset"){
TimingsHandler::reload();
$sender->sendMessage("Timings reset");
/*foreach(HandlerList::getHandlerLists() as $handlerList){
foreach($handlerList->getRegisteredListeners() as $listener){
if($listener instanceof TimedRegisteredListener){
$listener->reset();
}
}
}*/
}elseif($mode === "merged" or $mode === "report" or $paste){
$sampleTime = microtime(true) - self::$timingStart;
$index = 0;
$timingFolder = $sender->getServer()->getDataPath() . "timings/";
@mkdir($timingFolder, 0777);
$timings = $timingFolder . "timings.txt";
while(file_exists($timings)){
$timings = $timingFolder . "timings" . (++$index) . ".txt";
}
$fileTimings = $paste ? fopen("php://temp", "r+b") : fopen($timings, "a+b");
TimingsHandler::printTimings($fileTimings);
fwrite($fileTimings, "Sample time ". round($sampleTime * 1000000000) ." (". $sampleTime ."s)" . PHP_EOL);
if($paste){
fseek($fileTimings, 0);
$data = [
"public" => false,
"description" => "PocketMine-MP Timings",
"files" => [
"timings.txt" => [
"content" => stream_get_contents($fileTimings)
]
]
];
$ch = curl_init("https://api.github.com/gists");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
curl_setopt($ch, CURLOPT_FRESH_CONNECT, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data, JSON_UNESCAPED_SLASHES));
curl_setopt($ch, CURLOPT_AUTOREFERER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/json", "User-Agent: PocketMine-MP ".$sender->getServer()->getPocketMineVersion()]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$ret = curl_exec($ch);
$data = json_decode($ret);
curl_close($ch);
if($data === false or $data === null or !isset($data->html_url)){
$sender->sendMessage("An error happened while pasting the report");
return true;
}
$timings = $data->html_url;
}
fclose($fileTimings);
$sender->sendMessage("Timings written to ". $timings);
$sender->sendMessage("Paste contents of file into form at http://aikar.co/timings.php to read results.");
}
return true;
}
}

View File

@ -33,6 +33,7 @@ use pocketmine\event\entity\EntityMotionEvent;
use pocketmine\event\entity\EntityMoveEvent; use pocketmine\event\entity\EntityMoveEvent;
use pocketmine\event\entity\EntitySpawnEvent; use pocketmine\event\entity\EntitySpawnEvent;
use pocketmine\event\entity\EntityTeleportEvent; use pocketmine\event\entity\EntityTeleportEvent;
use pocketmine\event\Timings;
use pocketmine\level\format\Chunk; use pocketmine\level\format\Chunk;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\level\Position; use pocketmine\level\Position;
@ -42,6 +43,8 @@ use pocketmine\metadata\Metadatable;
use pocketmine\metadata\MetadataValue; use pocketmine\metadata\MetadataValue;
use pocketmine\nbt\tag\Byte; use pocketmine\nbt\tag\Byte;
use pocketmine\nbt\tag\Compound; use pocketmine\nbt\tag\Compound;
use pocketmine\nbt\tag\Double;
use pocketmine\nbt\tag\Enum;
use pocketmine\nbt\tag\Float; use pocketmine\nbt\tag\Float;
use pocketmine\nbt\tag\Short; use pocketmine\nbt\tag\Short;
use pocketmine\network\protocol\MoveEntityPacket; use pocketmine\network\protocol\MoveEntityPacket;
@ -201,22 +204,28 @@ abstract class Entity extends Position implements Metadatable{
} }
public function saveNBT(){ public function saveNBT(){
$this->namedtag["Pos"][0] = $this->x; $this->namedtag->Pos = new Enum("Pos", [
$this->namedtag["Pos"][1] = $this->y; new Double(0, $this->x),
$this->namedtag["Pos"][2] = $this->z; new Double(1, $this->y),
new Double(2, $this->z)
]);
$this->namedtag["Motion"][0] = $this->motionX; $this->namedtag->Motion = new Enum("Motion", [
$this->namedtag["Motion"][1] = $this->motionY; new Double(0, $this->motionX),
$this->namedtag["Motion"][2] = $this->motionZ; new Double(1, $this->motionY),
new Double(2, $this->motionZ)
]);
$this->namedtag["Rotation"][0] = $this->yaw; $this->namedtag->Rotation = new Enum("Rotation", [
$this->namedtag["Rotation"][1] = $this->pitch; new Float(0, $this->yaw),
new Float(1, $this->pitch)
]);
$this->namedtag["FallDistance"] = $this->fallDistance; $this->namedtag->FallDistance = new Float("FallDistance", $this->fallDistance);
$this->namedtag["Fire"] = $this->fireTicks; $this->namedtag->Fire = new Short("Fire", $this->fireTicks);
$this->namedtag["Air"] = $this->airTicks; $this->namedtag->Air = new Short("Air", $this->airTicks);
$this->namedtag["OnGround"] = $this->onGround == true ? 1 : 0; $this->namedtag->OnGround = new Byte("OnGround", $this->onGround == true ? 1 : 0);
$this->namedtag["Invulnerable"] = $this->invulnerable == true ? 1 : 0; $this->namedtag->Invulnerable = new Byte("Invulnerable", $this->invulnerable == true ? 1 : 0);
} }
protected abstract function initEntity(); protected abstract function initEntity();
@ -669,6 +678,8 @@ abstract class Entity extends Position implements Metadatable{
return; return;
} }
Timings::$entityMoveTimer->startTiming();
$ox = $this->x; $ox = $this->x;
$oy = $this->y; $oy = $this->y;
$oz = $this->z; $oz = $this->z;
@ -832,6 +843,7 @@ abstract class Entity extends Position implements Metadatable{
//TODO: vehicle collision events (first we need to spawn them!) //TODO: vehicle collision events (first we need to spawn them!)
Timings::$entityMoveTimer->stopTiming();
} }

View File

@ -21,6 +21,7 @@
namespace pocketmine\entity; namespace pocketmine\entity;
use pocketmine\inventory\Inventory;
use pocketmine\inventory\InventoryHolder; use pocketmine\inventory\InventoryHolder;
use pocketmine\inventory\PlayerInventory; use pocketmine\inventory\PlayerInventory;
use pocketmine\item\Item; use pocketmine\item\Item;
@ -83,47 +84,36 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
parent::saveNBT(); parent::saveNBT();
$this->namedtag->Inventory = new Enum("Inventory", []); $this->namedtag->Inventory = new Enum("Inventory", []);
$this->namedtag->Inventory->setTagType(NBT::TAG_Compound); $this->namedtag->Inventory->setTagType(NBT::TAG_Compound);
for($slot = 0; $slot < 9; ++$slot){ if($this->inventory instanceof PlayerInventory){
$hotbarSlot = $this->inventory->getHotbarSlotIndex($slot); for($slot = 0; $slot < 9; ++$slot){
if($hotbarSlot !== -1){ $hotbarSlot = $this->inventory->getHotbarSlotIndex($slot);
$item = $this->inventory->getItem($hotbarSlot); if($hotbarSlot !== -1){
if($item->getID() !== 0 and $item->getCount() > 0){ $item = $this->inventory->getItem($hotbarSlot);
$this->namedtag->Inventory[$slot] = new Compound(false, array( if($item->getID() !== 0 and $item->getCount() > 0){
new Byte("Count", $item->getCount()), $this->namedtag->Inventory[$slot] = new Compound(false, array(
new Short("Damage", $item->getDamage()), new Byte("Count", $item->getCount()),
new Byte("Slot", $slot), new Short("Damage", $item->getDamage()),
new Byte("TrueSlot", $hotbarSlot), new Byte("Slot", $slot),
new Short("id", $item->getID()), new Byte("TrueSlot", $hotbarSlot),
)); new Short("id", $item->getID()),
continue; ));
continue;
}
} }
$this->namedtag->Inventory[$slot] = new Compound(false, array(
new Byte("Count", 0),
new Short("Damage", 0),
new Byte("Slot", $slot),
new Byte("TrueSlot", -1),
new Short("id", 0),
));
} }
$this->namedtag->Inventory[$slot] = new Compound(false, array(
new Byte("Count", 0),
new Short("Damage", 0),
new Byte("Slot", $slot),
new Byte("TrueSlot", -1),
new Short("id", 0),
));
}
//Normal inventory //Normal inventory
$slotCount = Player::SURVIVAL_SLOTS + 9; $slotCount = Player::SURVIVAL_SLOTS + 9;
//$slotCount = (($this instanceof Player and ($this->gamemode & 0x01) === 1) ? Player::CREATIVE_SLOTS : Player::SURVIVAL_SLOTS) + 9; //$slotCount = (($this instanceof Player and ($this->gamemode & 0x01) === 1) ? Player::CREATIVE_SLOTS : Player::SURVIVAL_SLOTS) + 9;
for($slot = 9; $slot < $slotCount; ++$slot){ for($slot = 9; $slot < $slotCount; ++$slot){
$item = $this->inventory->getItem($slot - 9); $item = $this->inventory->getItem($slot - 9);
$this->namedtag->Inventory[$slot] = new Compound(false, array(
new Byte("Count", $item->getCount()),
new Short("Damage", $item->getDamage()),
new Byte("Slot", $slot),
new Short("id", $item->getID()),
));
}
//Armor
for($slot = 100; $slot < 104; ++$slot){
$item = $this->inventory->getItem($this->inventory->getSize() + $slot - 100);
if($item instanceof Item and $item->getID() !== Item::AIR){
$this->namedtag->Inventory[$slot] = new Compound(false, array( $this->namedtag->Inventory[$slot] = new Compound(false, array(
new Byte("Count", $item->getCount()), new Byte("Count", $item->getCount()),
new Short("Damage", $item->getDamage()), new Short("Damage", $item->getDamage()),
@ -131,6 +121,19 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
new Short("id", $item->getID()), new Short("id", $item->getID()),
)); ));
} }
//Armor
for($slot = 100; $slot < 104; ++$slot){
$item = $this->inventory->getItem($this->inventory->getSize() + $slot - 100);
if($item instanceof Item and $item->getID() !== Item::AIR){
$this->namedtag->Inventory[$slot] = new Compound(false, array(
new Byte("Count", $item->getCount()),
new Short("Damage", $item->getDamage()),
new Byte("Slot", $slot),
new Short("id", $item->getID()),
));
}
}
} }
} }

View File

@ -26,6 +26,7 @@ use pocketmine\event\entity\EntityDamageByEntityEvent;
use pocketmine\event\entity\EntityDamageEvent; use pocketmine\event\entity\EntityDamageEvent;
use pocketmine\event\entity\EntityDeathEvent; use pocketmine\event\entity\EntityDeathEvent;
use pocketmine\event\entity\EntityRegainHealthEvent; use pocketmine\event\entity\EntityRegainHealthEvent;
use pocketmine\event\Timings;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
use pocketmine\nbt\tag\Short; use pocketmine\nbt\tag\Short;
use pocketmine\network\protocol\EntityEventPacket; use pocketmine\network\protocol\EntityEventPacket;
@ -58,6 +59,23 @@ abstract class Living extends Entity implements Damageable{
public abstract function getName(); public abstract function getName();
public function attack($damage, $source = EntityDamageEvent::CAUSE_MAGIC){ public function attack($damage, $source = EntityDamageEvent::CAUSE_MAGIC){
if($this instanceof Player and ($this->getGamemode() & 0x01) === 1){
if($source instanceof EntityDamageEvent){
$cause = $source->getCause();
}else{
$cause = $source;
}
if(
$cause !== EntityDamageEvent::CAUSE_MAGIC
and $cause !== EntityDamageEvent::CAUSE_SUICIDE
and $cause !== EntityDamageEvent::CAUSE_VOID
){
return;
}
}
//TODO: attack tick limit //TODO: attack tick limit
$pk = new EntityEventPacket(); $pk = new EntityEventPacket();
$pk->eid = $this->getID(); $pk->eid = $this->getID();
@ -97,10 +115,16 @@ abstract class Living extends Entity implements Damageable{
} }
} }
public function entityBaseTick(){
Timings::$timerEntityBaseTick->startTiming();
parent::entityBaseTick();
Timings::$timerEntityBaseTick->stopTiming();
}
/** /**
* @return Item[] * @return Item[]
*/ */
public function getDrops(){ public function getDrops(){
return []; return [];
} }
} }

View File

@ -0,0 +1,103 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
namespace pocketmine\event;
use pocketmine\level\Level;
class LevelTimings{
/** @var TimingsHandler */
public $mobSpawn;
/** @var TimingsHandler */
public $doChunkUnload;
/** @var TimingsHandler */
public $doPortalForcer;
/** @var TimingsHandler */
public $doTickPending;
/** @var TimingsHandler */
public $doTickTiles;
/** @var TimingsHandler */
public $doVillages;
/** @var TimingsHandler */
public $doChunkMap;
/** @var TimingsHandler */
public $doChunkGC;
/** @var TimingsHandler */
public $doSounds;
/** @var TimingsHandler */
public $entityTick;
/** @var TimingsHandler */
public $tileEntityTick;
/** @var TimingsHandler */
public $tileEntityPending;
/** @var TimingsHandler */
public $tracker;
/** @var TimingsHandler */
public $doTick;
/** @var TimingsHandler */
public $tickEntities;
/** @var TimingsHandler */
public $syncChunkLoadTimer;
/** @var TimingsHandler */
public $syncChunkLoadDataTimer;
/** @var TimingsHandler */
public $syncChunkLoadStructuresTimer;
/** @var TimingsHandler */
public $syncChunkLoadEntitiesTimer;
/** @var TimingsHandler */
public $syncChunkLoadTileEntitiesTimer;
/** @var TimingsHandler */
public $syncChunkLoadTileTicksTimer;
/** @var TimingsHandler */
public $syncChunkLoadPostTimer;
public function __construct(Level $level){
$name = $level->getFolderName() . " - ";
$this->mobSpawn = new TimingsHandler("** ". $name ."mobSpawn");
$this->doChunkUnload = new TimingsHandler("** ". $name ."doChunkUnload");
$this->doTickPending = new TimingsHandler("** ". $name ."doTickPending");
$this->doTickTiles = new TimingsHandler("** ". $name ."doTickTiles");
$this->doVillages = new TimingsHandler("** ". $name ."doVillages");
$this->doChunkMap = new TimingsHandler("** ". $name ."doChunkMap");
$this->doSounds = new TimingsHandler("** ". $name ."doSounds");
$this->doChunkGC = new TimingsHandler("** ". $name ."doChunkGC");
$this->doPortalForcer = new TimingsHandler("** ". $name ."doPortalForcer");
$this->entityTick = new TimingsHandler("** ". $name ."entityTick");
$this->tileEntityTick = new TimingsHandler("** ". $name ."tileEntityTick");
$this->tileEntityPending = new TimingsHandler("** ". $name ."tileEntityPending");
$this->syncChunkLoadTimer = new TimingsHandler("** ". $name ."syncChunkLoad");
$this->syncChunkLoadDataTimer = new TimingsHandler("** ". $name ."syncChunkLoad - Data");
$this->syncChunkLoadStructuresTimer = new TimingsHandler("** ". $name ."syncChunkLoad - Structures");
$this->syncChunkLoadEntitiesTimer = new TimingsHandler("** ". $name ."syncChunkLoad - Entities");
$this->syncChunkLoadTileEntitiesTimer = new TimingsHandler("** ". $name ."syncChunkLoad - TileEntities");
$this->syncChunkLoadTileTicksTimer = new TimingsHandler("** ". $name ."syncChunkLoad - TileTicks");
$this->syncChunkLoadPostTimer = new TimingsHandler("** ". $name ."syncChunkLoad - Post");
$this->tracker = new TimingsHandler($name ."tracker");
$this->doTick = new TimingsHandler($name ."doTick");
$this->tickEntities = new TimingsHandler($name ."tickEntities");
}
}

View File

@ -0,0 +1,182 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
namespace pocketmine\event;
use pocketmine\entity\Entity;
use pocketmine\plugin\PluginManager;
use pocketmine\scheduler\PluginTask;
use pocketmine\scheduler\TaskHandler;
use pocketmine\tile\Tile;
abstract class Timings{
/** @var TimingsHandler */
public static $serverTickTimer;
/** @var TimingsHandler */
public static $playerListTimer;
/** @var TimingsHandler */
public static $connectionTimer;
/** @var TimingsHandler */
public static $tickablesTimer;
/** @var TimingsHandler */
public static $schedulerTimer;
/** @var TimingsHandler */
public static $chunkIOTickTimer;
/** @var TimingsHandler */
public static $timeUpdateTimer;
/** @var TimingsHandler */
public static $serverCommandTimer;
/** @var TimingsHandler */
public static $worldSaveTimer;
/** @var TimingsHandler */
public static $entityMoveTimer;
/** @var TimingsHandler */
public static $tickEntityTimer;
/** @var TimingsHandler */
public static $activatedEntityTimer;
/** @var TimingsHandler */
public static $tickTileEntityTimer;
/** @var TimingsHandler */
public static $timerEntityBaseTick;
/** @var TimingsHandler */
public static $timerEntityAI;
/** @var TimingsHandler */
public static $timerEntityAICollision;
/** @var TimingsHandler */
public static $timerEntityAIMove;
/** @var TimingsHandler */
public static $timerEntityTickRest;
/** @var TimingsHandler */
public static $processQueueTimer;
/** @var TimingsHandler */
public static $schedulerSyncTimer;
/** @var TimingsHandler */
public static $playerCommandTimer;
/** @var TimingsHandler[] */
public static $entityTypeTimingMap = [];
/** @var TimingsHandler[] */
public static $tileEntityTypeTimingMap = [];
/** @var TimingsHandler[] */
public static $pluginTaskTimingMap = [];
public static function init(){
if(self::$serverTickTimer instanceof TimingsHandler){
return;
}
self::$serverTickTimer = new TimingsHandler("** Full Server Tick");
self::$playerListTimer = new TimingsHandler("Player List");
self::$connectionTimer = new TimingsHandler("Connection Handler");
self::$tickablesTimer = new TimingsHandler("Tickables");
self::$schedulerTimer = new TimingsHandler("Scheduler");
self::$chunkIOTickTimer = new TimingsHandler("ChunkIOTick");
self::$timeUpdateTimer = new TimingsHandler("Time Update");
self::$serverCommandTimer = new TimingsHandler("Server Command");
self::$worldSaveTimer = new TimingsHandler("World Save");
self::$entityMoveTimer = new TimingsHandler("** entityMove");
self::$tickEntityTimer = new TimingsHandler("** tickEntity");
self::$activatedEntityTimer = new TimingsHandler("** activatedTickEntity");
self::$tickTileEntityTimer = new TimingsHandler("** tickTileEntity");
self::$timerEntityBaseTick = new TimingsHandler("** livingEntityBaseTick");
self::$timerEntityAI = new TimingsHandler("** livingEntityAI");
self::$timerEntityAICollision = new TimingsHandler("** livingEntityAICollision");
self::$timerEntityAIMove = new TimingsHandler("** livingEntityAIMove");
self::$timerEntityTickRest = new TimingsHandler("** livingEntityTickRest");
self::$processQueueTimer = new TimingsHandler("processQueue");
self::$schedulerSyncTimer = new TimingsHandler("** Scheduler - Sync Tasks", PluginManager::$pluginParentTimer);
self::$playerCommandTimer = new TimingsHandler("** playerCommand");
}
/**
* @param TaskHandler $task
* @param $period
*
* @return TimingsHandler
*/
public static function getPluginTaskTimings(TaskHandler $task, $period){
$ftask = $task->getTask();
if($ftask instanceof PluginTask and $ftask->getOwner() !== null){
$plugin = $ftask->getOwner()->getDescription()->getName();
}elseif($task->timingName !== null){
$plugin = "Scheduler";
}else{
$plugin = "Unknown";
}
$taskname = $task->getTaskName();
$name = "Task: ". $plugin." Runnable: ". $taskname;
if($period > 0){
$name .= "(interval:".$period.")";
}else{
$name .= "(Single)";
}
if(!isset(self::$pluginTaskTimingMap[$name])){
self::$pluginTaskTimingMap[$name] = new TimingsHandler($name, self::$schedulerSyncTimer);
}
return self::$pluginTaskTimingMap[$name];
}
/**
* @param Entity $entity
*
* @return TimingsHandler
*/
public static function getEntityTimings(Entity $entity){
$entityType = (new \ReflectionClass($entity))->getShortName();
if(!isset(self::$entityTypeTimingMap[$entityType])){
self::$entityTypeTimingMap[$entityType] = new TimingsHandler("** tickEntity - ". $entityType, self::$activatedEntityTimer);
}
return self::$entityTypeTimingMap[$entityType];
}
/**
* @param Tile $tile
*
* @return TimingsHandler
*/
public static function getTileEntityTimings(Tile $tile){
$tileType = (new \ReflectionClass($tile))->getShortName();
if(!isset(self::$tileEntityTypeTimingMap[$tileType])){
self::$tileEntityTypeTimingMap[$tileType] = new TimingsHandler("** tickTileEntity - ". $tileType, self::$tickTileEntityTimer);
}
return self::$tileEntityTypeTimingMap[$tileType];
}
}

View File

@ -0,0 +1,151 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
namespace pocketmine\event;
use pocketmine\command\defaults\TimingsCommand;
use pocketmine\entity\Living;
use pocketmine\plugin\PluginManager;
use pocketmine\Server;
class TimingsHandler{
/** @var TimingsHandler[] */
private static $HANDLERS = [];
private $name;
/** @var TimingsHandler */
private $parent = null;
private $count = 0;
private $start = 0;
private $timingDepth = 0;
private $totalTime = 0;
private $curTickTotal = 0;
private $violations = 0;
/**
* @param string $name
* @param TimingsHandler $parent
*/
public function __construct($name, $parent = null){
$this->name = $name;
if($parent instanceof TimingsHandler){
$this->parent = $parent;
}
self::$HANDLERS[spl_object_hash($this)] = $this;
}
public static function printTimings($fp){
fwrite($fp, "Minecraft" . PHP_EOL);
foreach(self::$HANDLERS as $timings){
$time = $timings->totalTime;
$count = $timings->count;
if($count === 0){
continue;
}
$avg = $time / $count;
fwrite($fp, " " . $timings->name ." Time: ". round($time * 1000000000) ." Count: ". $count ." Avg: ". round($avg * 1000000000) ." Violations: ". $timings->violations . PHP_EOL);
}
fwrite($fp, "# Version " . Server::getInstance()->getVersion() . PHP_EOL);
fwrite($fp, "# PocketMine-MP " . Server::getInstance()->getPocketMineVersion() . PHP_EOL);
$entities = 0;
$livingEntities = 0;
foreach(Server::getInstance()->getLevels() as $level){
$entities += count($level->getEntities());
foreach($level->getEntities() as $e){
if($e instanceof Living){
++$livingEntities;
}
}
}
fwrite($fp, "# Entities ". $entities . PHP_EOL);
fwrite($fp, "# LivingEntities ". $livingEntities . PHP_EOL);
}
public static function reload(){
if(Server::getInstance()->getPluginManager()->useTimings()){
foreach(self::$HANDLERS as $timings){
$timings->reset();
}
TimingsCommand::$timingStart = microtime(true);
}
}
public static function tick(){
if(PluginManager::$useTimings){
foreach(self::$HANDLERS as $timings){
if($timings->curTickTotal > 0.05){
$timings->violations += ceil($timings->curTickTotal / 0.05);
}
$timings->curTickTotal = 0;
$timings->timingDepth = 0;
}
}
}
public function startTiming(){
if(PluginManager::$useTimings and ++$this->timingDepth === 1){
$this->start = microtime(true);
if($this->parent instanceof TimingsHandler and ++$this->parent->timingDepth === 1){
$this->parent->start = $this->start;
}
}
}
public function stopTiming(){
if(PluginManager::$useTimings){
if(--$this->timingDepth !== 0 or $this->start === 0){
return;
}
$diff = microtime(true) - $this->start;
$this->totalTime += $diff;
$this->curTickTotal += $diff;
$this->count++;
$this->start = 0;
if($this->parent instanceof TimingsHandler){
$this->parent->stopTiming();
}
}
}
public function reset(){
$this->count = 0;
$this->violations = 0;
$this->curTickTotal = 0;
$this->totalTime = 0;
$this->start = 0;
$this->timingDepth = 0;
}
public function remove(){
unset(self::$HANDLERS[spl_object_hash($this)]);
}
}

View File

@ -33,6 +33,7 @@ use pocketmine\event\block\BlockPlaceEvent;
use pocketmine\event\level\LevelSaveEvent; use pocketmine\event\level\LevelSaveEvent;
use pocketmine\event\level\LevelUnloadEvent; use pocketmine\event\level\LevelUnloadEvent;
use pocketmine\event\level\SpawnChangeEvent; use pocketmine\event\level\SpawnChangeEvent;
use pocketmine\event\LevelTimings;
use pocketmine\event\player\PlayerInteractEvent; use pocketmine\event\player\PlayerInteractEvent;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\level\format\Chunk; use pocketmine\level\format\Chunk;
@ -157,6 +158,9 @@ class Level implements ChunkManager, Metadatable{
Block::BEETROOT_BLOCK, Block::BEETROOT_BLOCK,
]; ];
/** @var LevelTimings */
public $timings;
/** /**
* Returns the chunk unique hash/key * Returns the chunk unique hash/key
* *
@ -208,6 +212,7 @@ class Level implements ChunkManager, Metadatable{
$this->chunksPerTick = (int) $this->server->getProperty("chunk-ticking.per-tick", 128); $this->chunksPerTick = (int) $this->server->getProperty("chunk-ticking.per-tick", 128);
$this->chunkTickList = []; $this->chunkTickList = [];
$this->clearChunksOnTick = (bool) $this->server->getProperty("chunk-ticking.clear-tick-list", true); $this->clearChunksOnTick = (bool) $this->server->getProperty("chunk-ticking.clear-tick-list", true);
$this->timings = new LevelTimings($this);
} }
/** /**
@ -336,7 +341,7 @@ class Level implements ChunkManager, Metadatable{
*/ */
public function freeAllChunks(Player $player){ public function freeAllChunks(Player $player){
foreach($this->usedChunks as $i => $c){ foreach($this->usedChunks as $i => $c){
unset($this->usedChunks[$i][spl_object_hash($player)]); unset($this->usedChunks[$i][$player->getID()]);
} }
} }
@ -349,7 +354,7 @@ class Level implements ChunkManager, Metadatable{
* @param Player $player * @param Player $player
*/ */
public function freeChunk($X, $Z, Player $player){ public function freeChunk($X, $Z, Player $player){
unset($this->usedChunks[Level::chunkHash($X, $Z)][$player->getID()]); unset($this->usedChunks[$index = Level::chunkHash($X, $Z)][$player->getID()]);
$this->unloadChunkRequest($X, $Z, true); $this->unloadChunkRequest($X, $Z, true);
} }
@ -386,6 +391,8 @@ class Level implements ChunkManager, Metadatable{
*/ */
public function doTick($currentTick){ public function doTick($currentTick){
$this->timings->doTick->startTiming();
if(($currentTick % 200) === 0){ if(($currentTick % 200) === 0){
$this->checkTime(); $this->checkTime();
} }
@ -440,19 +447,21 @@ class Level implements ChunkManager, Metadatable{
$X = null; $X = null;
$Z = null; $Z = null;
//Do chunk updates //Do block updates
$this->timings->doTickPending->startTiming();
while($this->updateQueue->count() > 0 and $this->updateQueue->current()["priority"] <= $currentTick){ while($this->updateQueue->count() > 0 and $this->updateQueue->current()["priority"] <= $currentTick){
$block = $this->getBlock($this->updateQueue->extract()["data"]); $block = $this->getBlock($this->updateQueue->extract()["data"]);
$block->onUpdate(self::BLOCK_UPDATE_SCHEDULED); $block->onUpdate(self::BLOCK_UPDATE_SCHEDULED);
} }
$this->timings->doTickPending->stopTiming();
$this->timings->doTickTiles->startTiming();
$this->tickChunks(); $this->tickChunks();
$this->timings->doTickTiles->stopTiming();
$this->processChunkRequest(); $this->processChunkRequest();
if($this->nextSave < microtime(true)){ $this->timings->doTick->stopTiming();
$this->save(false);
}
} }
private function tickChunks(){ private function tickChunks(){
@ -1481,7 +1490,9 @@ class Level implements ChunkManager, Metadatable{
if($chunk instanceof Chunk){ if($chunk instanceof Chunk){
return true; return true;
}else{ }else{
$this->timings->syncChunkLoadTimer->startTiming();
$this->provider->loadChunk($x, $z); $this->provider->loadChunk($x, $z);
$this->timings->syncChunkLoadTimer->stopTiming();
return $this->provider->getChunk($x, $z) instanceof Chunk; return $this->provider->getChunk($x, $z) instanceof Chunk;
} }
@ -1509,10 +1520,14 @@ class Level implements ChunkManager, Metadatable{
if($safe === true and $this->isChunkInUse($x, $z)){ if($safe === true and $this->isChunkInUse($x, $z)){
return false; return false;
} }
$this->timings->doChunkUnload->startTiming();
$this->provider->unloadChunk($x, $z, $safe); $this->provider->unloadChunk($x, $z, $safe);
unset($this->usedChunks[Level::chunkHash($x, $z)]);
Cache::remove("world:" . $this->getID() . ":$x:$z"); Cache::remove("world:" . $this->getID() . ":$x:$z");
$this->timings->doChunkUnload->stopTiming();
return true; return true;
} }
@ -1668,7 +1683,7 @@ class Level implements ChunkManager, Metadatable{
} }
public function regenerateChunk($x, $z){ public function regenerateChunk($x, $z){
$this->unloadChunk($x, $z); $this->unloadChunk($x, $z, false);
$this->cancelUnloadChunkRequest($x, $z); $this->cancelUnloadChunkRequest($x, $z);
@ -1677,6 +1692,8 @@ class Level implements ChunkManager, Metadatable{
} }
public function doChunkGarbageCollection(){ public function doChunkGarbageCollection(){
$this->timings->doChunkGC->startTiming();
$X = null; $X = null;
$Z = null; $Z = null;
@ -1698,11 +1715,17 @@ class Level implements ChunkManager, Metadatable{
if(count($c) === 0){ if(count($c) === 0){
Level::getXZ($i, $X, $Z); Level::getXZ($i, $X, $Z);
if(!$this->isSpawnChunk($X, $Z)){ if(!$this->isSpawnChunk($X, $Z)){
if($this->getAutoSave()){
$this->provider->saveChunk($X, $Z);
}
$this->unloadChunk($X, $Z, true); $this->unloadChunk($X, $Z, true);
} }
} }
} }
$this->timings->doChunkGC->stopTiming();
} }

View File

@ -117,7 +117,10 @@ class Anvil extends BaseLevelProvider{
$regionX = $regionZ = null; $regionX = $regionZ = null;
self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ); self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ);
$this->loadRegion($regionX, $regionZ); $this->loadRegion($regionX, $regionZ);
$this->level->timings->syncChunkLoadDataTimer->startTiming();
$chunk = $this->getRegion($regionX, $regionZ)->readChunk($chunkX - $regionX * 32, $chunkZ - $regionZ * 32, $create); //generate empty chunk if not loaded $chunk = $this->getRegion($regionX, $regionZ)->readChunk($chunkX - $regionX * 32, $chunkZ - $regionZ * 32, $create); //generate empty chunk if not loaded
$this->level->timings->syncChunkLoadDataTimer->stopTiming();
if($chunk instanceof Chunk){ if($chunk instanceof Chunk){
$this->chunks[$index] = $chunk; $this->chunks[$index] = $chunk;
@ -127,8 +130,8 @@ class Anvil extends BaseLevelProvider{
} }
public function unloadChunk($x, $z, $safe = true){ public function unloadChunk($x, $z, $safe = true){
$chunk = $this->getChunk($x, $z, false);
if($safe === true and $this->isChunkLoaded($x, $z)){ if($safe === true and $this->isChunkLoaded($x, $z)){
$chunk = $this->getChunk($x, $z);
foreach($chunk->getEntities() as $entity){ foreach($chunk->getEntities() as $entity){
if($entity instanceof Player){ if($entity instanceof Player){
return false; return false;
@ -136,7 +139,17 @@ class Anvil extends BaseLevelProvider{
} }
} }
unset($this->chunks[Level::chunkHash($x, $z)]); foreach($chunk->getEntities() as $entity){
$entity->close();
}
foreach($chunk->getTiles() as $tile){
$tile->close();
}
$this->chunks[$index = Level::chunkHash($x, $z)] = null;
unset($this->chunks[$index]);
return true; return true;
} }

View File

@ -116,13 +116,14 @@ class RegionLoader{
$this->writeLocationIndex($index); $this->writeLocationIndex($index);
}elseif($compression !== self::COMPRESSION_ZLIB and $compression !== self::COMPRESSION_GZIP){ }elseif($compression !== self::COMPRESSION_ZLIB and $compression !== self::COMPRESSION_GZIP){
trigger_error("Invalid compression type", E_USER_WARNING); trigger_error("Invalid compression type", E_USER_WARNING);
return false; return false;
} }
$nbt = new NBT(NBT::BIG_ENDIAN); $nbt = new NBT(NBT::BIG_ENDIAN);
$nbt->readCompressed(fread($this->filePointer, $length - 1), $compression); $nbt->readCompressed(fread($this->filePointer, $length - 1), $compression);
$chunk = $nbt->getData(); $chunk = $nbt->getData();
if(!isset($chunk->Level) or !($chunk->Level instanceof Compound)){ if(!isset($chunk->Level) or !($chunk->Level instanceof Compound)){
return false; return false;
} }

View File

@ -98,6 +98,7 @@ abstract class BaseChunk implements Chunk{
$this->biomeColors = array_fill(0, 256, Binary::readInt("\x00\x85\xb2\x4a")); $this->biomeColors = array_fill(0, 256, Binary::readInt("\x00\x85\xb2\x4a"));
} }
$this->getLevel()->getLevel()->timings->syncChunkLoadEntitiesTimer->startTiming();
foreach($entities as $nbt){ foreach($entities as $nbt){
if($nbt instanceof Compound){ if($nbt instanceof Compound){
if(!isset($nbt->id)){ if(!isset($nbt->id)){
@ -115,8 +116,9 @@ abstract class BaseChunk implements Chunk{
} }
} }
} }
$this->getLevel()->getLevel()->timings->syncChunkLoadEntitiesTimer->stopTiming();
$this->getLevel()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->startTiming();
foreach($tiles as $nbt){ foreach($tiles as $nbt){
if($nbt instanceof Compound){ if($nbt instanceof Compound){
if(!isset($nbt->id)){ if(!isset($nbt->id)){
@ -135,6 +137,7 @@ abstract class BaseChunk implements Chunk{
} }
} }
} }
$this->getLevel()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->stopTiming();
} }
public function getX(){ public function getX(){

View File

@ -80,6 +80,11 @@ class Vector3{
return $this->z; return $this->z;
} }
/**
* @param Vector3|int $x
* @param int $y
* @param int $z
*/
public function add($x, $y = 0, $z = 0){ public function add($x, $y = 0, $z = 0){
if($x instanceof Vector3){ if($x instanceof Vector3){
return $this->add($x->x, $x->y, $x->z); return $this->add($x->x, $x->y, $x->z);
@ -88,6 +93,12 @@ class Vector3{
} }
} }
/**
* @param Vector3|int $x
* @param int $y
* @param int $z
* @return Vector3
*/
public function subtract($x = 0, $y = 0, $z = 0){ public function subtract($x = 0, $y = 0, $z = 0){
if($x instanceof Vector3){ if($x instanceof Vector3){
return $this->add(-$x->x, -$x->y, -$x->z); return $this->add(-$x->x, -$x->y, -$x->z);
@ -190,4 +201,4 @@ class Vector3{
return "Vector3(x=" . $this->x . ",y=" . $this->y . ",z=" . $this->z . ")"; return "Vector3(x=" . $this->x . ",y=" . $this->y . ",z=" . $this->z . ")";
} }
} }

View File

@ -107,6 +107,7 @@ abstract class DefaultPermissions{
self::registerPermission(new Permission(self::ROOT . ".command.defaultgamemode", "Allows the user to change the default gamemode", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.defaultgamemode", "Allows the user to change the default gamemode", Permission::DEFAULT_OP), $commands);
self::registerPermission(new Permission(self::ROOT . ".command.seed", "Allows the user to view the seed of the world", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.seed", "Allows the user to view the seed of the world", Permission::DEFAULT_OP), $commands);
self::registerPermission(new Permission(self::ROOT . ".command.status", "Allows the user to view the server performance", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.status", "Allows the user to view the server performance", Permission::DEFAULT_OP), $commands);
self::registerPermission(new Permission(self::ROOT . ".command.timings", "Allows the user to records timings for all plugin events", Permission::DEFAULT_OP), $commands);
self::registerPermission(new Permission(self::ROOT . ".command.spawnpoint", "Allows the user to change player's spawnpoint", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.spawnpoint", "Allows the user to change player's spawnpoint", Permission::DEFAULT_OP), $commands);
self::registerPermission(new Permission(self::ROOT . ".command.setworldspawn", "Allows the user to change the world spawn", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.setworldspawn", "Allows the user to change the world spawn", Permission::DEFAULT_OP), $commands);

View File

@ -35,4 +35,8 @@ class MethodEventExecutor implements EventExecutor{
public function execute(Listener $listener, Event $event){ public function execute(Listener $listener, Event $event){
call_user_func(array($listener, $this->method), $event); call_user_func(array($listener, $this->method), $event);
} }
public function getMethod(){
return $this->method;
}
} }

View File

@ -21,12 +21,14 @@
namespace pocketmine\plugin; namespace pocketmine\plugin;
use pocketmine\command\defaults\TimingsCommand;
use pocketmine\command\PluginCommand; use pocketmine\command\PluginCommand;
use pocketmine\command\SimpleCommandMap; use pocketmine\command\SimpleCommandMap;
use pocketmine\event\Event; use pocketmine\event\Event;
use pocketmine\event\EventPriority; use pocketmine\event\EventPriority;
use pocketmine\event\HandlerList; use pocketmine\event\HandlerList;
use pocketmine\event\Listener; use pocketmine\event\Listener;
use pocketmine\event\TimingsHandler;
use pocketmine\permission\Permissible; use pocketmine\permission\Permissible;
use pocketmine\permission\Permission; use pocketmine\permission\Permission;
use pocketmine\Server; use pocketmine\Server;
@ -82,6 +84,11 @@ class PluginManager{
*/ */
protected $fileAssociations = []; protected $fileAssociations = [];
/** @var TimingsHandler */
public static $pluginParentTimer;
public static $useTimings = false;
/** /**
* @param Server $server * @param Server $server
* @param SimpleCommandMap $commandMap * @param SimpleCommandMap $commandMap
@ -306,8 +313,10 @@ class PluginManager{
} }
} }
TimingsCommand::$timingStart = microtime(true);
return $loadedPlugins; return $loadedPlugins;
}else{ }else{
TimingsCommand::$timingStart = microtime(true);
return []; return [];
} }
} }
@ -699,7 +708,9 @@ class PluginManager{
throw new \Exception("Plugin attempted to register " . $event . " while not enabled"); throw new \Exception("Plugin attempted to register " . $event . " while not enabled");
} }
$this->getEventListeners($event)->register(new RegisteredListener($listener, $executor, $priority, $plugin, $ignoreCancelled)); $timings = new TimingsHandler("Plugin: ".$plugin->getDescription()->getName()." Event: ".get_class($listener)."::".($executor instanceof MethodEventExecutor ? $executor->getMethod() : "???")."(".(new \ReflectionClass($event))->getShortName().")", self::$pluginParentTimer);
$this->getEventListeners($event)->register(new RegisteredListener($listener, $executor, $priority, $plugin, $ignoreCancelled, $timings));
} }
/** /**
@ -715,4 +726,18 @@ class PluginManager{
return $event::$handlerList; return $event::$handlerList;
} }
/**
* @return bool
*/
public function useTimings(){
return self::$useTimings;
}
/**
* @param bool $use
*/
public function setUseTimings($use){
self::$useTimings = (bool) $use;
}
} }

View File

@ -24,6 +24,7 @@ namespace pocketmine\plugin;
use pocketmine\event\Cancellable; use pocketmine\event\Cancellable;
use pocketmine\event\Event; use pocketmine\event\Event;
use pocketmine\event\Listener; use pocketmine\event\Listener;
use pocketmine\event\TimingsHandler;
class RegisteredListener{ class RegisteredListener{
@ -42,19 +43,26 @@ class RegisteredListener{
/** @var bool */ /** @var bool */
private $ignoreCancelled; private $ignoreCancelled;
/** @var TimingsHandler */
private $timings;
/** /**
* @param Listener $listener * @param Listener $listener
* @param EventExecutor $executor * @param EventExecutor $executor
* @param int $priority * @param int $priority
* @param Plugin $plugin * @param Plugin $plugin
* @param boolean $ignoreCancelled * @param boolean $ignoreCancelled
* @param TimingsHandler $timings
*/ */
public function __construct(Listener $listener, EventExecutor $executor, $priority, Plugin $plugin, $ignoreCancelled){ public function __construct(Listener $listener, EventExecutor $executor, $priority, Plugin $plugin, $ignoreCancelled, TimingsHandler $timings){
$this->listener = $listener; $this->listener = $listener;
$this->priority = $priority; $this->priority = $priority;
$this->plugin = $plugin; $this->plugin = $plugin;
$this->executor = $executor; $this->executor = $executor;
$this->ignoreCancelled = $ignoreCancelled; $this->ignoreCancelled = $ignoreCancelled;
$this->timings = $timings;
} }
/** /**
@ -85,7 +93,13 @@ class RegisteredListener{
if($event instanceof Cancellable and $event->isCancelled() and $this->isIgnoringCancelled()){ if($event instanceof Cancellable and $event->isCancelled() and $this->isIgnoringCancelled()){
return; return;
} }
$this->timings->startTiming();
$this->executor->execute($this->listener, $event); $this->executor->execute($this->listener, $event);
$this->timings->stopTiming();
}
public function __destruct(){
$this->timings->remove();
} }
/** /**

View File

@ -4,9 +4,9 @@
settings: settings:
shutdown-message: "Server closed" shutdown-message: "Server closed"
plugin-profiling: false
query-plugins: true query-plugins: true
deprecated-verbose: true deprecated-verbose: true
enable-profiling: false
advanced-cache: false advanced-cache: false
upnp-forwarding: false upnp-forwarding: false
send-usage: true send-usage: true

View File

@ -0,0 +1,30 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
*
*
*/
namespace pocketmine\scheduler;
class PHPGarbageCollectionTask extends Task{
public function onRun($currentTicks){
gc_collect_cycles();
}
}

View File

@ -178,7 +178,7 @@ class ServerScheduler{
$period = 1; $period = 1;
} }
return $this->handle(new TaskHandler($task, $this->nextId(), $delay, $period)); return $this->handle(new TaskHandler(get_class($task), $task, $this->nextId(), $delay, $period));
} }
private function handle(TaskHandler $handler){ private function handle(TaskHandler $handler){
@ -201,12 +201,15 @@ class ServerScheduler{
public function mainThreadHeartbeat($currentTick){ public function mainThreadHeartbeat($currentTick){
$this->currentTick = $currentTick; $this->currentTick = $currentTick;
while($this->isReady($this->currentTick)){ while($this->isReady($this->currentTick)){
/** @var TaskHandler $task */
$task = $this->queue->extract(); $task = $this->queue->extract();
if($task->isCancelled()){ if($task->isCancelled()){
unset($this->tasks[$task->getTaskId()]); unset($this->tasks[$task->getTaskId()]);
continue; continue;
}else{ }else{
$task->timings->startTiming();
$task->run($this->currentTick); $task->run($this->currentTick);
$task->timings->stopTiming();
} }
if($task->isRepeating()){ if($task->isRepeating()){
$task->setNextRun($this->currentTick + $task->getPeriod()); $task->setNextRun($this->currentTick + $task->getPeriod());

View File

@ -21,6 +21,8 @@
namespace pocketmine\scheduler; namespace pocketmine\scheduler;
use pocketmine\event\Timings;
class TaskHandler{ class TaskHandler{
/** @var Task */ /** @var Task */
@ -41,17 +43,25 @@ class TaskHandler{
/** @var bool */ /** @var bool */
protected $cancelled = false; protected $cancelled = false;
/** @var \pocketmine\event\TimingsHandler */
public $timings;
public $timingName = null;
/** /**
* @param Task $task * @param string $timingName
* @param int $taskId * @param Task $task
* @param int $delay * @param int $taskId
* @param int $period * @param int $delay
* @param int $period
*/ */
public function __construct(Task $task, $taskId, $delay = -1, $period = -1){ public function __construct($timingName, Task $task, $taskId, $delay = -1, $period = -1){
$this->task = $task; $this->task = $task;
$this->taskId = $taskId; $this->taskId = $taskId;
$this->delay = $delay; $this->delay = $delay;
$this->period = $period; $this->period = $period;
$this->timingName = $timingName === null ? "Unknown" : $timingName;
$this->timings = Timings::getPluginTaskTimings($this, $period);
} }
/** /**
@ -127,6 +137,7 @@ class TaskHandler{
public function remove(){ public function remove(){
$this->cancelled = true; $this->cancelled = true;
$this->task->setHandler(null); $this->task->setHandler(null);
$this->timings->remove();
} }
/** /**
@ -135,4 +146,14 @@ class TaskHandler{
public function run($currentTick){ public function run($currentTick){
$this->task->onRun($currentTick); $this->task->onRun($currentTick);
} }
/**
* @return string
*/
public function getTaskName(){
if($this->timingName !== null){
return $this->timingName;
}
return get_class($this->task);
}
} }

View File

@ -25,6 +25,7 @@
*/ */
namespace pocketmine\tile; namespace pocketmine\tile;
use pocketmine\event\Timings;
use pocketmine\level\format\Chunk; use pocketmine\level\format\Chunk;
use pocketmine\level\Position; use pocketmine\level\Position;
use pocketmine\nbt\tag\Compound; use pocketmine\nbt\tag\Compound;
@ -57,6 +58,9 @@ abstract class Tile extends Position{
protected $lastUpdate; protected $lastUpdate;
protected $server; protected $server;
/** @var \pocketmine\event\TimingsHandler */
public $tickTimer;
public function __construct(Chunk $chunk, Compound $nbt){ public function __construct(Chunk $chunk, Compound $nbt){
$this->server = $chunk->getLevel()->getLevel()->getServer(); $this->server = $chunk->getLevel()->getLevel()->getServer();
$this->chunk = $chunk; $this->chunk = $chunk;
@ -72,6 +76,7 @@ abstract class Tile extends Position{
$this->chunk->addTile($this); $this->chunk->addTile($this);
$this->getLevel()->addTile($this); $this->getLevel()->addTile($this);
$this->tickTimer = Timings::getTileEntityTimings($this);
} }
public function getID(){ public function getID(){

@ -1 +1 @@
Subproject commit 577c371aa07bd2aa3cedc3b7d4bcdc1d54999c70 Subproject commit 243e611b935242ca33c082fee83c0f1f5cddaaf7