diff --git a/src/pocketmine/CrashDump.php b/src/pocketmine/CrashDump.php index 5d30473d4..5bfc6b569 100644 --- a/src/pocketmine/CrashDump.php +++ b/src/pocketmine/CrashDump.php @@ -143,7 +143,7 @@ class CrashDump{ $error = $lastExceptionError; }else{ $error = (array) error_get_last(); - $error["trace"] = @getTrace(3); + $error["trace"] = getTrace(4); //Skipping CrashDump->baseCrash, CrashDump->construct, Server->crashDump $errorConversion = [ E_ERROR => "E_ERROR", E_WARNING => "E_WARNING", diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 324404d0f..a6aaed00e 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -3722,15 +3722,6 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->dataPacket($pk); } - public function setHealth($amount){ - parent::setHealth($amount); - if($this->spawned === true){ - $pk = new SetHealthPacket(); - $pk->health = $this->getHealth(); - $this->dataPacket($pk); - } - } - public function attack($damage, EntityDamageEvent $source){ if(!$this->isAlive()){ return; diff --git a/src/pocketmine/PocketMine.php b/src/pocketmine/PocketMine.php index 9e6d42fac..6c3724637 100644 --- a/src/pocketmine/PocketMine.php +++ b/src/pocketmine/PocketMine.php @@ -357,7 +357,7 @@ namespace pocketmine { return -1; } - function getTrace($start = 1, $trace = null){ + function getTrace($start = 0, $trace = null){ if($trace === null){ if(function_exists("xdebug_get_function_stack")){ $trace = array_reverse(xdebug_get_function_stack()); diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 9f0db3dbc..a882b9b97 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -158,6 +158,9 @@ class Server{ private $currentTPS = 20; private $currentUse = 0; + /** @var bool */ + private $doTitleTick = true; + private $sendUsageTicker = 0; private $dispatchSignals = false; @@ -1466,6 +1469,8 @@ class Server{ $this->alwaysTickPlayers = (int) $this->getProperty("level-settings.always-tick-players", false); $this->baseTickRate = (int) $this->getProperty("level-settings.base-tick-rate", 1); + $this->doTitleTick = (bool) $this->getProperty("console.title-tick", true); + $this->scheduler = new ServerScheduler(); if($this->getConfigBoolean("enable-rcon", false) === true){ @@ -2040,9 +2045,8 @@ class Server{ $errline = $e->getLine(); $type = ($errno === E_ERROR or $errno === E_USER_ERROR) ? \LogLevel::ERROR : (($errno === E_USER_WARNING or $errno === E_WARNING) ? \LogLevel::WARNING : \LogLevel::NOTICE); - if(($pos = strpos($errstr, "\n")) !== false){ - $errstr = substr($errstr, 0, $pos); - } + + $errstr = preg_replace('/\s+/', ' ', trim($errstr)); $errfile = cleanPath($errfile); @@ -2054,7 +2058,7 @@ class Server{ "fullFile" => $e->getFile(), "file" => $errfile, "line" => $errline, - "trace" => @getTrace(1, $trace) + "trace" => getTrace(0, $trace) ]; global $lastExceptionError, $lastError; @@ -2263,7 +2267,9 @@ class Server{ } public function sendUsage($type = SendUsageTask::TYPE_STATUS){ - $this->scheduler->scheduleAsyncTask(new SendUsageTask($this, $type, $this->uniquePlayers)); + if($this->getProperty("anonymous-statistics.enabled", true)){ + $this->scheduler->scheduleAsyncTask(new SendUsageTask($this, $type, $this->uniquePlayers)); + } $this->uniquePlayers = []; } @@ -2297,10 +2303,6 @@ class Server{ } private function titleTick(){ - if(!Terminal::hasFormattingCodes()){ - return; - } - $d = Utils::getRealMemoryUsage(); $u = Utils::getMemoryUsage(true); @@ -2376,7 +2378,9 @@ class Server{ } if(($this->tickCounter & 0b1111) === 0){ - $this->titleTick(); + if($this->doTitleTick and Terminal::hasFormattingCodes()){ + $this->titleTick(); + } $this->currentTPS = 20; $this->currentUse = 0; @@ -2441,4 +2445,13 @@ class Server{ return true; } + + /** + * Called when something attempts to serialize the server instance. + * + * @throws \BadMethodCallException because Server instances cannot be serialized + */ + public function __sleep(){ + throw new \BadMethodCallException("Cannot serialize Server instance"); + } } diff --git a/src/pocketmine/block/GlowingObsidian.php b/src/pocketmine/block/GlowingObsidian.php index e3ee02ba8..26b55cfd2 100644 --- a/src/pocketmine/block/GlowingObsidian.php +++ b/src/pocketmine/block/GlowingObsidian.php @@ -22,7 +22,7 @@ namespace pocketmine\block; -class GlowingObsidian extends Transparent{ +class GlowingObsidian extends Solid{ protected $id = self::GLOWING_OBSIDIAN; diff --git a/src/pocketmine/block/RedstoneTorch.php b/src/pocketmine/block/RedstoneTorch.php index ca7f7f352..32e4bb77a 100644 --- a/src/pocketmine/block/RedstoneTorch.php +++ b/src/pocketmine/block/RedstoneTorch.php @@ -32,4 +32,8 @@ class RedstoneTorch extends Torch{ public function getName(){ return "Redstone Torch"; } + + public function getLightLevel(){ + return 7; + } } diff --git a/src/pocketmine/block/Torch.php b/src/pocketmine/block/Torch.php index c71bd957a..78cf35e82 100644 --- a/src/pocketmine/block/Torch.php +++ b/src/pocketmine/block/Torch.php @@ -34,7 +34,7 @@ class Torch extends Flowable{ } public function getLightLevel(){ - return 15; + return 14; } public function getName(){ diff --git a/src/pocketmine/command/SimpleCommandMap.php b/src/pocketmine/command/SimpleCommandMap.php index 48e30d1ac..ae959c8ad 100644 --- a/src/pocketmine/command/SimpleCommandMap.php +++ b/src/pocketmine/command/SimpleCommandMap.php @@ -136,7 +136,7 @@ class SimpleCommandMap implements CommandMap{ if($label === null){ $label = $command->getName(); } - $label = strtolower(trim($label)); + $label = trim($label); $fallbackPrefix = strtolower(trim($fallbackPrefix)); $registered = $this->registerAlias($command, false, $fallbackPrefix, $label); @@ -188,7 +188,7 @@ class SimpleCommandMap implements CommandMap{ * @return Command|null */ public function matchCommand(string &$commandName, array &$args){ - $count = max(count($args), 255); + $count = min(count($args), 255); for($i = 0; $i < $count; ++$i){ $commandName .= array_shift($args); diff --git a/src/pocketmine/entity/Entity.php b/src/pocketmine/entity/Entity.php index 95b02fcad..a3cc10d98 100644 --- a/src/pocketmine/entity/Entity.php +++ b/src/pocketmine/entity/Entity.php @@ -470,7 +470,7 @@ abstract class Entity extends Location implements Metadatable{ if(isset($this->effects[$effect->getId()])){ $oldEffect = $this->effects[$effect->getId()]; if( - abs($effect->getAmplifier()) <= ($oldEffect->getAmplifier()) + abs($effect->getAmplifier()) < ($oldEffect->getAmplifier()) or (abs($effect->getAmplifier()) === abs($oldEffect->getAmplifier()) and $effect->getDuration() < $oldEffect->getDuration()) ){ diff --git a/src/pocketmine/entity/Human.php b/src/pocketmine/entity/Human.php index 509ff8122..ac56f66ad 100644 --- a/src/pocketmine/entity/Human.php +++ b/src/pocketmine/entity/Human.php @@ -408,6 +408,12 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ public function saveNBT(){ parent::saveNBT(); + + $this->namedtag->foodLevel = new IntTag("foodLevel", $this->getFood()); + $this->namedtag->foodExhaustionLevel = new FloatTag("foodExhaustionLevel", $this->getExhaustion()); + $this->namedtag->foodSaturationLevel = new FloatTag("foodSaturationLevel", $this->getSaturation()); + $this->namedtag->foodTickTimer = new IntTag("foodTickTimer", $this->foodTickTimer); + $this->namedtag->Inventory = new ListTag("Inventory", []); $this->namedtag->Inventory->setTagType(NBT::TAG_Compound); if($this->inventory !== null){ @@ -437,7 +443,9 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ $slotCount = $this->inventory->getSize() + $this->inventory->getHotbarSize(); for($slot = $this->inventory->getHotbarSize(); $slot < $slotCount; ++$slot){ $item = $this->inventory->getItem($slot - 9); - $this->namedtag->Inventory[$slot] = $item->nbtSerialize($slot); + if($item->getId() !== ItemItem::AIR){ + $this->namedtag->Inventory[$slot] = $item->nbtSerialize($slot); + } } //Armor diff --git a/src/pocketmine/event/LevelTimings.php b/src/pocketmine/event/LevelTimings.php index ef78dcbd2..52a0ce6fb 100644 --- a/src/pocketmine/event/LevelTimings.php +++ b/src/pocketmine/event/LevelTimings.php @@ -25,6 +25,13 @@ use pocketmine\level\Level; class LevelTimings{ + /** @var TimingsHandler */ + public $setBlock; + /** @var TimingsHandler */ + public $doBlockLightUpdates; + /** @var TimingsHandler */ + public $doBlockSkyLightUpdates; + /** @var TimingsHandler */ public $mobSpawn; /** @var TimingsHandler */ @@ -79,6 +86,10 @@ class LevelTimings{ public function __construct(Level $level){ $name = $level->getFolderName() . " - "; + $this->setBlock = new TimingsHandler("** " . $name . "setBlock"); + $this->doBlockLightUpdates = new TimingsHandler("** " . $name . "doBlockLightUpdates"); + $this->doBlockSkyLightUpdates = new TimingsHandler("** " . $name . "doBlockSkyLightUpdates"); + $this->mobSpawn = new TimingsHandler("** " . $name . "mobSpawn"); $this->doChunkUnload = new TimingsHandler("** " . $name . "doChunkUnload"); $this->doTickPending = new TimingsHandler("** " . $name . "doTickPending"); diff --git a/src/pocketmine/item/Item.php b/src/pocketmine/item/Item.php index c5ef917a8..356466d68 100644 --- a/src/pocketmine/item/Item.php +++ b/src/pocketmine/item/Item.php @@ -65,13 +65,19 @@ class Item implements ItemIds, \JsonSerializable{ /** @var \SplFixedArray */ public static $list = null; + /** @var Block|null */ protected $block; + /** @var int */ protected $id; + /** @var int */ protected $meta; + /** @var string */ private $tags = ""; + /** @var CompoundTag|null */ private $cachedNBT = null; + /** @var int */ public $count; - protected $durability = 0; + /** @var string */ protected $name; public function canBeActivated(){ @@ -1007,4 +1013,12 @@ class Item implements ItemIds, \JsonSerializable{ return $item; } + public function __clone(){ + if($this->block !== null){ + $this->block = clone $this->block; + } + + $this->cachedNBT = null; + } + } diff --git a/src/pocketmine/item/ItemBlock.php b/src/pocketmine/item/ItemBlock.php index 4bcac3f9d..8a61b31aa 100644 --- a/src/pocketmine/item/ItemBlock.php +++ b/src/pocketmine/item/ItemBlock.php @@ -37,10 +37,6 @@ class ItemBlock extends Item{ $this->block->setDamage($this->meta !== -1 ? $this->meta : 0); } - public function __clone(){ - $this->block = clone $this->block; - } - public function getBlock() : Block{ return $this->block; } diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index e16d13ad8..96ab3d836 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -1286,17 +1286,42 @@ class Level implements ChunkManager, Metadatable{ } public function updateBlockSkyLight(int $x, int $y, int $z){ + $this->timings->doBlockSkyLightUpdates->startTiming(); //TODO + $this->timings->doBlockSkyLightUpdates->stopTiming(); + } + + /** + * Returns the highest light level available in the positions adjacent to the specified block coordinates. + * + * @param int $x + * @param int $y + * @param int $z + * + * @return int + */ + public function getHighestAdjacentBlockLight(int $x, int $y, int $z) : int{ + return max([ + $this->getBlockLightAt($x + 1, $y, $z), + $this->getBlockLightAt($x - 1, $y, $z), + $this->getBlockLightAt($x, $y + 1, $z), + $this->getBlockLightAt($x, $y - 1, $z), + $this->getBlockLightAt($x, $y, $z + 1), + $this->getBlockLightAt($x, $y, $z - 1) + ]); } public function updateBlockLight(int $x, int $y, int $z){ + $this->timings->doBlockLightUpdates->startTiming(); + $lightPropagationQueue = new \SplQueue(); $lightRemovalQueue = new \SplQueue(); $visited = []; $removalVisited = []; + $id = $this->getBlockIdAt($x, $y, $z); $oldLevel = $this->getBlockLightAt($x, $y, $z); - $newLevel = (int) Block::$light[$this->getBlockIdAt($x, $y, $z)]; + $newLevel = max(Block::$light[$id], $this->getHighestAdjacentBlockLight($x, $y, $z) - Block::$lightFilter[$id]); if($oldLevel !== $newLevel){ $this->setBlockLightAt($x, $y, $z, $newLevel); @@ -1328,7 +1353,7 @@ class Level implements ChunkManager, Metadatable{ /** @var Vector3 $node */ $node = $lightPropagationQueue->dequeue(); - $lightLevel = $this->getBlockLightAt($node->x, $node->y, $node->z) - (int) Block::$lightFilter[$this->getBlockIdAt($node->x, $node->y, $node->z)]; + $lightLevel = $this->getBlockLightAt($node->x, $node->y, $node->z); if($lightLevel >= 1){ $this->computeSpreadBlockLight($node->x - 1, $node->y, $node->z, $lightLevel, $lightPropagationQueue, $visited); @@ -1339,6 +1364,8 @@ class Level implements ChunkManager, Metadatable{ $this->computeSpreadBlockLight($node->x, $node->y, $node->z + 1, $lightLevel, $lightPropagationQueue, $visited); } } + + $this->timings->doBlockLightUpdates->stopTiming(); } private function computeRemoveBlockLight(int $x, int $y, int $z, int $currentLight, \SplQueue $queue, \SplQueue $spreadQueue, array &$visited, array &$spreadVisited){ @@ -1365,6 +1392,7 @@ class Level implements ChunkManager, Metadatable{ private function computeSpreadBlockLight(int $x, int $y, int $z, int $currentLight, \SplQueue $queue, array &$visited){ if($y < 0) return; $current = $this->getBlockLightAt($x, $y, $z); + $currentLight -= Block::$lightFilter[$this->getBlockIdAt($x, $y, $z)]; if($current < $currentLight){ $this->setBlockLightAt($x, $y, $z, $currentLight); @@ -1402,6 +1430,8 @@ class Level implements ChunkManager, Metadatable{ return false; } + $this->timings->setBlock->startTiming(); + if($this->getChunk($pos->x >> 4, $pos->z >> 4, true)->setBlock($pos->x & 0x0f, $pos->y & Level::Y_MASK, $pos->z & 0x0f, $block->getId(), $block->getDamage())){ if(!($pos instanceof Position)){ $pos = $this->temporalPosition->setComponents($pos->x, $pos->y, $pos->z); @@ -1441,9 +1471,13 @@ class Level implements ChunkManager, Metadatable{ $this->updateAround($pos); } + $this->timings->setBlock->stopTiming(); + return true; } + $this->timings->setBlock->stopTiming(); + return false; } @@ -1508,7 +1542,7 @@ class Level implements ChunkManager, Metadatable{ if(($player->isSurvival() and $item instanceof Item and !$target->isBreakable($item)) or $player->isSpectator()){ $ev->setCancelled(); - }elseif(!$player->isOp() and ($distance = $this->server->getSpawnRadius()) > -1){ + }elseif(!$player->hasPermission("pocketmine.spawnprotect.bypass") and ($distance = $this->server->getSpawnRadius()) > -1){ $t = new Vector2($target->x, $target->z); $s = new Vector2($this->getSpawnLocation()->x, $this->getSpawnLocation()->z); if(count($this->server->getOps()->getAll()) > 0 and $t->distance($s) <= $distance){ //set it to cancelled so plugins can bypass this @@ -1645,7 +1679,7 @@ class Level implements ChunkManager, Metadatable{ if($player !== null){ $ev = new PlayerInteractEvent($player, $item, $target, $face, $target->getId() === 0 ? PlayerInteractEvent::RIGHT_CLICK_AIR : PlayerInteractEvent::RIGHT_CLICK_BLOCK); - if(!$player->isOp() and ($distance = $this->server->getSpawnRadius()) > -1){ + if(!$player->hasPermission("pocketmine.spawnprotect.bypass") and ($distance = $this->server->getSpawnRadius()) > -1){ $t = new Vector2($target->x, $target->z); $s = new Vector2($this->getSpawnLocation()->x, $this->getSpawnLocation()->z); if(count($this->server->getOps()->getAll()) > 0 and $t->distance($s) <= $distance){ //set it to cancelled so plugins can bypass this @@ -1735,7 +1769,7 @@ class Level implements ChunkManager, Metadatable{ if($player !== null){ $ev = new BlockPlaceEvent($player, $hand, $block, $target, $item); - if(!$player->isOp() and ($distance = $this->server->getSpawnRadius()) > -1){ + if(!$player->hasPermission("pocketmine.spawnprotect.bypass") and ($distance = $this->server->getSpawnRadius()) > -1){ $t = new Vector2($target->x, $target->z); $s = new Vector2($this->getSpawnLocation()->x, $this->getSpawnLocation()->z); if(count($this->server->getOps()->getAll()) > 0 and $t->distance($s) <= $distance){ //set it to cancelled so plugins can bypass this diff --git a/src/pocketmine/math/Vector3.php b/src/pocketmine/math/Vector3.php index 36801282a..395ee9c1c 100644 --- a/src/pocketmine/math/Vector3.php +++ b/src/pocketmine/math/Vector3.php @@ -250,7 +250,7 @@ class Vector3{ if($f < 0 or $f > 1){ return null; }else{ - return new Vector3($this->x + $xDiff * $f, $this->y + $yDiff * $f, $this->z + $zDiff * $f); + return new Vector3($x, $this->y + $yDiff * $f, $this->z + $zDiff * $f); } } @@ -277,7 +277,7 @@ class Vector3{ if($f < 0 or $f > 1){ return null; }else{ - return new Vector3($this->x + $xDiff * $f, $this->y + $yDiff * $f, $this->z + $zDiff * $f); + return new Vector3($this->x + $xDiff * $f, $y, $this->z + $zDiff * $f); } } @@ -304,7 +304,7 @@ class Vector3{ if($f < 0 or $f > 1){ return null; }else{ - return new Vector3($this->x + $xDiff * $f, $this->y + $yDiff * $f, $this->z + $zDiff * $f); + return new Vector3($this->x + $xDiff * $f, $this->y + $yDiff * $f, $z); } } diff --git a/src/pocketmine/permission/DefaultPermissions.php b/src/pocketmine/permission/DefaultPermissions.php index b1aa2f682..079db04ff 100644 --- a/src/pocketmine/permission/DefaultPermissions.php +++ b/src/pocketmine/permission/DefaultPermissions.php @@ -47,12 +47,13 @@ abstract class DefaultPermissions{ $parent = self::registerPermission(new Permission(self::ROOT, "Allows using all PocketMine commands and utilities")); $broadcasts = self::registerPermission(new Permission(self::ROOT . ".broadcast", "Allows the user to receive all broadcast messages"), $parent); - self::registerPermission(new Permission(self::ROOT . ".broadcast.admin", "Allows the user to receive administrative broadcasts", Permission::DEFAULT_OP), $broadcasts); self::registerPermission(new Permission(self::ROOT . ".broadcast.user", "Allows the user to receive user broadcasts", Permission::DEFAULT_TRUE), $broadcasts); - $broadcasts->recalculatePermissibles(); + $spawnprotect = self::registerPermission(new Permission(self::ROOT . ".spawnprotect.bypass", "Allows the user to edit blocks within the protected spawn radius", Permission::DEFAULT_OP), $parent); + $spawnprotect->recalculatePermissibles(); + $commands = self::registerPermission(new Permission(self::ROOT . ".command", "Allows using all PocketMine commands"), $parent); $whitelist = self::registerPermission(new Permission(self::ROOT . ".command.whitelist", "Allows the user to modify the server whitelist", Permission::DEFAULT_OP), $commands); diff --git a/src/pocketmine/resources/pocketmine.yml b/src/pocketmine/resources/pocketmine.yml index 9b8df9222..132890293 100644 --- a/src/pocketmine/resources/pocketmine.yml +++ b/src/pocketmine/resources/pocketmine.yml @@ -185,6 +185,11 @@ timings: #Choose the host to use for viewing your timings results. host: mcpetimings.com +console: + #Choose whether to enable server stats reporting on the console title. + #NOTE: The title ticker will be disabled regardless if console colours are not enabled. + title-tick: true + aliases: #Examples: #showtheversion: version diff --git a/src/pocketmine/tile/Chest.php b/src/pocketmine/tile/Chest.php index 8d6abae94..3bffa58d6 100644 --- a/src/pocketmine/tile/Chest.php +++ b/src/pocketmine/tile/Chest.php @@ -63,6 +63,10 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{ foreach($this->getInventory()->getViewers() as $player){ $player->removeWindow($this->getRealInventory()); } + + $this->inventory = null; + $this->doubleInventory = null; + parent::close(); } } diff --git a/src/pocketmine/tile/Furnace.php b/src/pocketmine/tile/Furnace.php index d0c3e0806..cfb8e9da4 100644 --- a/src/pocketmine/tile/Furnace.php +++ b/src/pocketmine/tile/Furnace.php @@ -92,6 +92,9 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{ foreach($this->getInventory()->getViewers() as $player){ $player->removeWindow($this->getInventory()); } + + $this->inventory = null; + parent::close(); } } diff --git a/src/pocketmine/utils/MainLogger.php b/src/pocketmine/utils/MainLogger.php index 7afe1d627..ba69c40d5 100644 --- a/src/pocketmine/utils/MainLogger.php +++ b/src/pocketmine/utils/MainLogger.php @@ -134,12 +134,10 @@ class MainLogger extends \AttachableThreadedLogger{ $type = ($errno === E_ERROR or $errno === E_USER_ERROR) ? LogLevel::ERROR : (($errno === E_USER_WARNING or $errno === E_WARNING) ? LogLevel::WARNING : LogLevel::NOTICE); } $errno = $errorConversion[$errno] ?? $errno; - if(($pos = strpos($errstr, "\n")) !== false){ - $errstr = substr($errstr, 0, $pos); - } + $errstr = preg_replace('/\s+/', ' ', trim($errstr)); $errfile = \pocketmine\cleanPath($errfile); $this->log($type, get_class($e) . ": \"$errstr\" ($errno) in \"$errfile\" at line $errline"); - foreach(@\pocketmine\getTrace(1, $trace) as $i => $line){ + foreach(\pocketmine\getTrace(0, $trace) as $i => $line){ $this->debug($line); } }