diff --git a/.travis.yml b/.travis.yml index 91c927db6..e8d845709 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: php php: - - 7.2 - 7.3 before_script: diff --git a/BUILDING.md b/BUILDING.md index aac06d283..6853f5979 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -2,7 +2,7 @@ ## Pre-requisites - A bash shell (git bash is sufficient for Windows) - [`git`](https://git-scm.com) available in your shell -- PHP 7.2 or newer available in your shell +- PHP 7.3 or newer available in your shell - [`composer`](https://getcomposer.org) available in your shell ## Custom PHP binaries diff --git a/changelogs/3.13.md b/changelogs/3.13.md new file mode 100644 index 000000000..470fdb4fc --- /dev/null +++ b/changelogs/3.13.md @@ -0,0 +1,111 @@ +**For Minecraft: Bedrock Edition 1.14.60** + +This is a feature release, containing various minor API additions, deprecations and a few minor features. + +### Note about API versions +Plugins which don't touch the protocol and compatible with any previous 3.x.y version will also run on these releases and do not need API bumps. +Plugin developers should **only** update their required API to this version if you need the changes in this build. + +**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do. + +# 3.13.0 +## Core +- PHP 7.3.0 or newer is now required. +- Player movement processing has been revamped. It's now more tolerant of network lag and doesn't have as many problems with falling. + +## User Interface +- `/time` now supports additional aliases `noon`, `sunset`, `midnight` and `sunrise`. +- Removed warnings when a plugin registers a handler for a deprecated event. Since this warning is developer-focused, and too specific to be useful, it just caused annoyance and confusion to users who didn't know what it meant. + +## API +### General +- It's now possible to require a specific operating system using the `os` directive in `plugin.yml`. More information about this directive can be found in the [developer documentation](https://github.com/pmmp/DeveloperDocs). + +### Player +- `Player->resetItemCooldown()` now accepts a second parameter, allowing plugins to provide a custom duration. +- The following methods have been deprecated and have recommended replacements: + - `Player->addTitle()` -> `Player->sendTitle()` + - `Player->addSubTitle()` -> `Player->sendSubTitle()` + - `Player->addActionBarMessage()` -> `Player->sendActionBarMessage()` + +### Event +- The following methods have been deprecated: + - `EntityDespawnEvent->getType()` + - `EntityDespawnEvent->getPosition()` + - `EntityDespawnEvent->isCreature()` + - `EntityDespawnEvent->isHuman()` + - `EntityDespawnEvent->isProjectile()` + - `EntityDespawnEvent->isVehicle()` + - `EntityDespawnEvent->isItem()` + - `EntitySpawnEvent->getType()` + - `EntitySpawnEvent->getPosition()` + - `EntitySpawnEvent->isCreature()` + - `EntitySpawnEvent->isHuman()` + - `EntitySpawnEvent->isProjectile()` + - `EntitySpawnEvent->isVehicle()` + - `EntitySpawnEvent->isItem()` +- Added the following API methods: + - `EntityDeathEvent->getXpDropAmount()` + - `EntityDeathEvent->setXpDropAmount()` +- `PlayerDeathEvent::__construct()` now accepts a fourth (optional) parameter `int $xp`. +- `EntityDeathEvent::__construct()` now accepts a third (optional) parameter `int $xp`. + +### Inventory +- The following classes have been deprecated: + - `Recipe` +- The following methods have been deprecated: + - `CraftingManager->registerRecipe()` + - `Recipe->registerToCraftingManager()` (and all its implementations) + +### Item +- New `Enchantment` type ID constants have been added. +- `ItemFactory::fromStringSingle()` has been added. This works exactly the same as `ItemFactory::fromString()`, but it has a return type of `Item` instead of `Item|Item[]` (more static analysis friendly). + +### Level +- Added the following API methods: + - `Position->getLevelNonNull()`: this is the same as `Position->getLevel()`, but throws an `AssumptionFailedError` if the level is null or invalid (more static analysis friendly). + - `Level->getTimeOfDay()` +- The following constants have been changed: + - `Level::TIME_DAY` now has a value of `1000` + - `Level::TIME_NIGHT` now has a value of `13000` +- Added the following constants: + - `Level::TIME_MIDNIGHT` + - `Level::TIME_NOON` +- The following types of particles now accept optional `Color` parameters in the constructor: + - `EnchantParticle` + - `InstantEnchantParticle` + +### Network +- Added the following API methods: + - `RakLibInterface->setPacketLimit()` + +### Scheduler +AsyncTask thread-local storage has been improved, making it simpler and easier to use. +- `AsyncTask->fetchLocal()` no longer deletes stored thread-local data. Instead, the storage behaves more like properties, and gets deleted when the AsyncTask object goes out of scope. +- `AsyncTask->peekLocal()` has been `@deprecated` (use `fetchLocal()` instead). +- Notices are no longer emitted if an async task doesn't fetch its locally stored data. +- The following methods have been deprecated: + - `AsyncTask->getFromThreadStore()` (use its worker's corresponding method) + - `AsyncTask->saveToThreadStore()` (use its worker's corresponding method) + - `AsyncTask->removeFromThreadStore()` (use its worker's corresponding method) + +### Utils +- The following functions have been deprecated and have recommended replacements: + - `Utils::getMemoryUsage()` -> split into `Process::getMemoryUsage()` and `Process::getAdvancedMemoryUsage()` (not 1:1 replacement!!) + - `Utils::getRealMemoryUsage()` -> `Process::getRealMemoryUsage()` + - `Utils::getThreadCount()` -> `Process::getThreadCount()` + - `Utils::kill()` -> `Process::kill()` + - `Utils::execute()` -> `Process::execute()` +- Added the following constants: + - `Utils::OS_WINDOWS` + - `Utils::OS_IOS` + - `Utils::OS_MACOS` + - `Utils::OS_ANDROID` + - `Utils::OS_LINUX` + - `Utils::OS_BSD` + - `Utils::OS_UNKNOWN` +- Added the following API methods: + - `Config->getPath()` + - `Utils::recursiveUnlink()` + - `Terminal::write()` + - `Terminal::writeLine()` diff --git a/composer.json b/composer.json index 30c84e393..20e1574e4 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,7 @@ "homepage": "https://pmmp.io", "license": "LGPL-3.0", "require": { - "php": ">=7.2.0", + "php": ">=7.3.0", "php-64bit": "*", "ext-bcmath": "*", "ext-curl": "*", @@ -34,7 +34,8 @@ "pocketmine/log": "^0.2.0", "pocketmine/log-pthreads": "^0.1.0", "pocketmine/callback-validator": "^1.0.1", - "adhocore/json-comment": "^0.1.0" + "adhocore/json-comment": "^0.1.0", + "ocramius/package-versions": "^1.5" }, "require-dev": { "phpstan/phpstan": "^0.12.25", diff --git a/composer.lock b/composer.lock index afde9285d..87a5562ab 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "59c415a2cb668990b4a456b4bd106881", + "content-hash": "270aa35b0c4f554754b656129c5e8343", "packages": [ { "name": "adhocore/json-comment", @@ -50,6 +50,57 @@ ], "time": "2020-01-03T13:51:23+00:00" }, + { + "name": "ocramius/package-versions", + "version": "1.5.1", + "source": { + "type": "git", + "url": "https://github.com/Ocramius/PackageVersions.git", + "reference": "1d32342b8c1eb27353c8887c366147b4c2da673c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Ocramius/PackageVersions/zipball/1d32342b8c1eb27353c8887c366147b4c2da673c", + "reference": "1d32342b8c1eb27353c8887c366147b4c2da673c", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0.0", + "php": "^7.3.0" + }, + "require-dev": { + "composer/composer": "^1.8.6", + "doctrine/coding-standard": "^6.0.0", + "ext-zip": "*", + "infection/infection": "^0.13.4", + "phpunit/phpunit": "^8.2.5", + "vimeo/psalm": "^3.4.9" + }, + "type": "composer-plugin", + "extra": { + "class": "PackageVersions\\Installer", + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-4": { + "PackageVersions\\": "src/PackageVersions" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", + "time": "2019-07-17T15:49:50+00:00" + }, { "name": "pocketmine/binaryutils", "version": "0.1.11", @@ -2091,7 +2142,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=7.2.0", + "php": ">=7.3.0", "php-64bit": "*", "ext-bcmath": "*", "ext-curl": "*", diff --git a/src/pocketmine/CrashDump.php b/src/pocketmine/CrashDump.php index 321210dcd..9837b501c 100644 --- a/src/pocketmine/CrashDump.php +++ b/src/pocketmine/CrashDump.php @@ -23,13 +23,13 @@ declare(strict_types=1); namespace pocketmine; +use PackageVersions\Versions; use pocketmine\network\mcpe\protocol\ProtocolInfo; use pocketmine\plugin\PluginBase; use pocketmine\plugin\PluginLoadOrder; use pocketmine\plugin\PluginManager; use pocketmine\utils\Utils; use pocketmine\utils\VersionString; -use raklib\RakLib; use function base64_encode; use function date; use function error_get_last; @@ -88,7 +88,7 @@ class CrashDump{ * having their content changed, version format changing, etc. * It is not necessary to increase this when adding new fields. */ - private const FORMAT_VERSION = 2; + private const FORMAT_VERSION = 3; private const PLUGIN_INVOLVEMENT_NONE = "none"; private const PLUGIN_INVOLVEMENT_DIRECT = "direct"; @@ -117,10 +117,11 @@ class CrashDump{ mkdir($this->server->getDataPath() . "crashdumps"); } $this->path = $this->server->getDataPath() . "crashdumps/" . date("D_M_j-H.i.s-T_Y", $this->time) . ".log"; - $this->fp = @fopen($this->path, "wb"); - if(!is_resource($this->fp)){ + $fp = @fopen($this->path, "wb"); + if(!is_resource($fp)){ throw new \RuntimeException("Could not create Crash Dump"); } + $this->fp = $fp; $this->data["format_version"] = self::FORMAT_VERSION; $this->data["time"] = $this->time; $this->addLine($this->server->getName() . " Crash Dump " . date("D M j H:i:s T Y", $this->time)); @@ -228,7 +229,10 @@ class CrashDump{ if(isset($lastExceptionError)){ $error = $lastExceptionError; }else{ - $error = (array) error_get_last(); + $error = error_get_last(); + if($error === null){ + throw new \RuntimeException("Crash error information missing - did something use exit()?"); + } $error["trace"] = Utils::currentTrace(3); //Skipping CrashDump->baseCrash, CrashDump->construct, Server->crashDump $errorConversion = [ E_ERROR => "E_ERROR", @@ -288,9 +292,11 @@ class CrashDump{ if($this->server->getProperty("auto-report.send-code", true) !== false and file_exists($error["fullFile"])){ $file = @file($error["fullFile"], FILE_IGNORE_NEW_LINES); - for($l = max(0, $error["line"] - 10); $l < $error["line"] + 10 and isset($file[$l]); ++$l){ - $this->addLine("[" . ($l + 1) . "] " . $file[$l]); - $this->data["code"][$l + 1] = $file[$l]; + if($file !== false){ + for($l = max(0, $error["line"] - 10); $l < $error["line"] + 10 and isset($file[$l]); ++$l){ + $this->addLine("[" . ($l + 1) . "] " . $file[$l]); + $this->data["code"][$l + 1] = $file[$l]; + } } } @@ -339,18 +345,22 @@ class CrashDump{ $this->data["general"]["is_dev"] = \pocketmine\IS_DEVELOPMENT_BUILD; $this->data["general"]["protocol"] = ProtocolInfo::CURRENT_PROTOCOL; $this->data["general"]["git"] = \pocketmine\GIT_COMMIT; - $this->data["general"]["raklib"] = RakLib::VERSION; $this->data["general"]["uname"] = php_uname("a"); $this->data["general"]["php"] = phpversion(); $this->data["general"]["zend"] = zend_version(); $this->data["general"]["php_os"] = PHP_OS; $this->data["general"]["os"] = Utils::getOS(); + $this->data["general"]["composer_libraries"] = Versions::VERSIONS; $this->addLine($this->server->getName() . " version: " . $version->getFullVersion(true) . " [Protocol " . ProtocolInfo::CURRENT_PROTOCOL . "]"); $this->addLine("Git commit: " . \pocketmine\GIT_COMMIT); $this->addLine("uname -a: " . php_uname("a")); $this->addLine("PHP Version: " . phpversion()); $this->addLine("Zend version: " . zend_version()); $this->addLine("OS : " . PHP_OS . ", " . Utils::getOS()); + $this->addLine("Composer libraries: "); + foreach(Versions::VERSIONS as $library => $libraryVersion){ + $this->addLine("- $library $libraryVersion"); + } } /** diff --git a/src/pocketmine/MemoryManager.php b/src/pocketmine/MemoryManager.php index 20ec90997..91f46d291 100644 --- a/src/pocketmine/MemoryManager.php +++ b/src/pocketmine/MemoryManager.php @@ -27,6 +27,8 @@ use pocketmine\event\server\LowMemoryEvent; use pocketmine\scheduler\DumpWorkerMemoryTask; use pocketmine\scheduler\GarbageCollectionTask; use pocketmine\timings\Timings; +use pocketmine\utils\AssumptionFailedError; +use pocketmine\utils\Process; use pocketmine\utils\Utils; use function arsort; use function count; @@ -225,7 +227,7 @@ class MemoryManager{ if(($this->memoryLimit > 0 or $this->globalMemoryLimit > 0) and ++$this->checkTicker >= $this->checkRate){ $this->checkTicker = 0; - $memory = Utils::getMemoryUsage(true); + $memory = Process::getAdvancedMemoryUsage(); $trigger = false; if($this->memoryLimit > 0 and $memory[0] > $this->memoryLimit){ $trigger = 0; @@ -304,6 +306,7 @@ class MemoryManager{ */ public static function dumpMemory($startingObject, string $outputFolder, int $maxNesting, int $maxStringSize, \Logger $logger){ $hardLimit = ini_get('memory_limit'); + if($hardLimit === false) throw new AssumptionFailedError("memory_limit INI directive should always exist"); ini_set('memory_limit', '-1'); gc_disable(); @@ -403,8 +406,8 @@ class MemoryManager{ "properties" => [] ]; - if($reflection->getParentClass()){ - $info["parent"] = $reflection->getParentClass()->getName(); + if(($parent = $reflection->getParentClass()) !== false){ + $info["parent"] = $parent->getName(); } if(count($reflection->getInterfaceNames()) > 0){ diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 8e4402c79..49b4d8825 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -217,6 +217,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ public const SPECTATOR = 3; public const VIEW = Player::SPECTATOR; + private const MOVES_PER_TICK = 2; + private const MOVE_BACKLOG_SIZE = 100 * self::MOVES_PER_TICK; //100 ticks backlog (5 seconds) + private const RESOURCE_PACK_CHUNK_SIZE = 128 * 1024; //128KB /** @@ -331,10 +334,13 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ /** @var bool[] map: raw UUID (string) => bool */ protected $hiddenPlayers = []; + /** @var float */ + protected $moveRateLimit = 10 * self::MOVES_PER_TICK; + /** @var float|null */ + protected $lastMovementProcess = null; /** @var Vector3|null */ - protected $newPosition; - /** @var bool */ - protected $isTeleporting = false; + protected $forceMoveSync = null; + /** @var int */ protected $inAirTicks = 0; /** @var float */ @@ -524,7 +530,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } public function spawnTo(Player $player) : void{ - if($this->spawned and $player->spawned and $this->isAlive() and $player->isAlive() and $player->getLevel() === $this->level and $player->canSee($this) and !$this->isSpectator()){ + if($this->spawned and $player->spawned and $this->isAlive() and $player->isAlive() and $player->getLevelNonNull() === $this->level and $player->canSee($this) and !$this->isSpectator()){ parent::spawnTo($player); } } @@ -867,8 +873,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->lastPingMeasure = $pingMS; } + /** + * @deprecated + */ public function getNextPosition() : Position{ - return $this->newPosition !== null ? Position::fromObject($this->newPosition, $this->level) : $this->getPosition(); + return $this->getPosition(); } public function getInAirTicks() : int{ @@ -909,8 +918,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ /** * Resets the player's cooldown time for the given item back to the maximum. */ - public function resetItemCooldown(Item $item) : void{ - $ticks = $item->getCooldownTicks(); + public function resetItemCooldown(Item $item, ?int $ticks = null) : void{ + $ticks = $ticks ?? $item->getCooldownTicks(); if($ticks > 0){ $this->usedItemsCooldown[$item->getId()] = $this->server->getTick() + $ticks; } @@ -1214,7 +1223,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ if(!($pos instanceof Position)){ $level = $this->level; }else{ - $level = $pos->getLevel(); + $level = $pos->getLevelNonNull(); } $this->spawnPosition = new Position($pos->x, $pos->y, $pos->z, $level); $pk = new SetSpawnPositionPacket(); @@ -1530,23 +1539,19 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } } - /** - * @return void - */ - protected function processMovement(int $tickDiff){ - if(!$this->isAlive() or !$this->spawned or $this->newPosition === null or $this->isSleeping()){ + protected function handleMovement(Vector3 $newPos) : void{ + $this->moveRateLimit--; + if($this->moveRateLimit < 0){ return; } - assert($this->x !== null and $this->y !== null and $this->z !== null); - assert($this->newPosition->x !== null and $this->newPosition->y !== null and $this->newPosition->z !== null); - - $newPos = $this->newPosition; - $distanceSquared = $newPos->distanceSquared($this); + $oldPos = $this->asLocation(); + $distanceSquared = $newPos->distanceSquared($oldPos); $revert = false; - if(($distanceSquared / ($tickDiff ** 2)) > 100){ + if($distanceSquared > 100){ + //TODO: this is probably too big if we process every movement /* !!! BEWARE YE WHO ENTER HERE !!! * * This is NOT an anti-cheat check. It is a safety check. @@ -1558,7 +1563,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ * asking for help if you suffer the consequences of messing with this. */ $this->server->getLogger()->debug($this->getName() . " moved too fast, reverting movement"); - $this->server->getLogger()->debug("Old position: " . $this->asVector3() . ", new position: " . $this->newPosition); + $this->server->getLogger()->debug("Old position: " . $this->asVector3() . ", new position: " . $newPos); $revert = true; }elseif(!$this->level->isInLoadedTerrain($newPos) or !$this->level->isChunkGenerated($newPos->getFloorX() >> 4, $newPos->getFloorZ() >> 4)){ $revert = true; @@ -1572,7 +1577,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->move($dx, $dy, $dz); - $diff = $this->distanceSquared($newPos) / $tickDiff ** 2; + $diff = $this->distanceSquared($newPos); if($this->isSurvival() and $diff > 0.0625){ $ev = new PlayerIllegalMoveEvent($this, $newPos, new Vector3($this->lastX, $this->lastY, $this->lastZ)); @@ -1583,7 +1588,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ if(!$ev->isCancelled()){ $revert = true; $this->server->getLogger()->debug($this->getServer()->getLanguage()->translateString("pocketmine.player.invalidMove", [$this->getName()])); - $this->server->getLogger()->debug("Old position: " . $this->asVector3() . ", new position: " . $this->newPosition); + $this->server->getLogger()->debug("Old position: " . $this->asVector3() . ", new position: " . $newPos); } } @@ -1592,13 +1597,28 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } } + if($revert){ + $this->revertMovement($oldPos); + } + } + + /** + * Fires movement events and synchronizes player movement, every tick. + */ + protected function processMostRecentMovements() : void{ + $now = microtime(true); + $multiplier = $this->lastMovementProcess !== null ? ($now - $this->lastMovementProcess) * 20 : 1; + $exceededRateLimit = $this->moveRateLimit < 0; + $this->moveRateLimit = min(self::MOVE_BACKLOG_SIZE, max(0, $this->moveRateLimit) + self::MOVES_PER_TICK * $multiplier); + $this->lastMovementProcess = $now; + $from = new Location($this->lastX, $this->lastY, $this->lastZ, $this->lastYaw, $this->lastPitch, $this->level); $to = $this->getLocation(); $delta = (($this->lastX - $to->x) ** 2) + (($this->lastY - $to->y) ** 2) + (($this->lastZ - $to->z) ** 2); $deltaAngle = abs($this->lastYaw - $to->yaw) + abs($this->lastPitch - $to->pitch); - if(!$revert and ($delta > 0.0001 or $deltaAngle > 1.0)){ + if($delta > 0.0001 or $deltaAngle > 1.0){ $this->lastX = $to->x; $this->lastY = $to->y; $this->lastZ = $to->z; @@ -1610,41 +1630,47 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $ev->call(); - if(!($revert = $ev->isCancelled())){ //Yes, this is intended - if($to->distanceSquared($ev->getTo()) > 0.01){ //If plugins modify the destination - $this->teleport($ev->getTo()); - }else{ - $this->broadcastMovement(); - - $distance = sqrt((($from->x - $to->x) ** 2) + (($from->z - $to->z) ** 2)); - //TODO: check swimming (adds 0.015 exhaustion in MCPE) - if($this->isSprinting()){ - $this->exhaust(0.1 * $distance, PlayerExhaustEvent::CAUSE_SPRINTING); - }else{ - $this->exhaust(0.01 * $distance, PlayerExhaustEvent::CAUSE_WALKING); - } - } + if($ev->isCancelled()){ + $this->revertMovement($from); + return; } - } - if($revert){ + if($to->distanceSquared($ev->getTo()) > 0.01){ //If plugins modify the destination + $this->teleport($ev->getTo()); + return; + } - $this->lastX = $from->x; - $this->lastY = $from->y; - $this->lastZ = $from->z; + $this->broadcastMovement(); - $this->lastYaw = $from->yaw; - $this->lastPitch = $from->pitch; + $distance = sqrt((($from->x - $to->x) ** 2) + (($from->z - $to->z) ** 2)); + //TODO: check swimming (adds 0.015 exhaustion in MCPE) + if($this->isSprinting()){ + $this->exhaust(0.1 * $distance, PlayerExhaustEvent::CAUSE_SPRINTING); + }else{ + $this->exhaust(0.01 * $distance, PlayerExhaustEvent::CAUSE_WALKING); + } - $this->setPosition($from); - $this->sendPosition($from, $from->yaw, $from->pitch, MovePlayerPacket::MODE_RESET); - }else{ - if($distanceSquared != 0 and $this->nextChunkOrderRun > 20){ + if($this->nextChunkOrderRun > 20){ $this->nextChunkOrderRun = 20; } } - $this->newPosition = null; + if($exceededRateLimit){ //client and server positions will be out of sync if this happens + $this->server->getLogger()->debug("Player " . $this->getName() . " exceeded movement rate limit, forcing to last accepted position"); + $this->sendPosition($this, $this->yaw, $this->pitch, MovePlayerPacket::MODE_RESET); + } + } + + protected function revertMovement(Location $from) : void{ + $this->lastX = $from->x; + $this->lastY = $from->y; + $this->lastZ = $from->z; + + $this->lastYaw = $from->yaw; + $this->lastPitch = $from->pitch; + + $this->setPosition($from); + $this->sendPosition($from, $from->yaw, $from->pitch, MovePlayerPacket::MODE_RESET); } public function fall(float $fallDistance) : void{ @@ -1716,7 +1742,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->timings->startTiming(); if($this->spawned){ - $this->processMovement($tickDiff); + $this->processMostRecentMovements(); $this->motion->x = $this->motion->y = $this->motion->z = 0; //TODO: HACK! (Fixes player knockback being messed up) if($this->onGround){ $this->inAirTicks = 0; @@ -1876,12 +1902,25 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $animations = []; foreach($packet->clientData["AnimatedImageData"] as $animation){ - $animations[] = new SkinAnimation(new SkinImage($animation["ImageHeight"], $animation["ImageWidth"], base64_decode($animation["Image"], true)), $animation["Type"], $animation["Frames"]); + $animations[] = new SkinAnimation( + new SkinImage( + $animation["ImageHeight"], + $animation["ImageWidth"], + base64_decode($animation["Image"], true)), + $animation["Type"], + $animation["Frames"] + ); } $personaPieces = []; foreach($packet->clientData["PersonaPieces"] as $piece){ - $personaPieces[] = new PersonaSkinPiece($piece["PieceId"], $piece["PieceType"], $piece["PackId"], $piece["IsDefault"], $piece["ProductId"]); + $personaPieces[] = new PersonaSkinPiece( + $piece["PieceId"], + $piece["PieceType"], + $piece["PackId"], + $piece["IsDefault"], + $piece["ProductId"] + ); } $pieceTintColors = []; @@ -1892,9 +1931,17 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $skinData = new SkinData( $packet->clientData["SkinId"], base64_decode($packet->clientData["SkinResourcePatch"] ?? "", true), - new SkinImage($packet->clientData["SkinImageHeight"], $packet->clientData["SkinImageWidth"], base64_decode($packet->clientData["SkinData"], true)), + new SkinImage( + $packet->clientData["SkinImageHeight"], + $packet->clientData["SkinImageWidth"], + base64_decode($packet->clientData["SkinData"], true) + ), $animations, - new SkinImage($packet->clientData["CapeImageHeight"], $packet->clientData["CapeImageWidth"], base64_decode($packet->clientData["CapeData"] ?? "", true)), + new SkinImage( + $packet->clientData["CapeImageHeight"], + $packet->clientData["CapeImageWidth"], + base64_decode($packet->clientData["CapeData"] ?? "", true) + ), base64_decode($packet->clientData["SkinGeometryData"] ?? "", true), base64_decode($packet->clientData["SkinAnimationData"] ?? "", true), $packet->clientData["PremiumSkin"] ?? false, @@ -2251,8 +2298,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ public function handleMovePlayer(MovePlayerPacket $packet) : bool{ $newPos = $packet->position->round(4)->subtract(0, $this->baseOffset, 0); - if($this->isTeleporting and $newPos->distanceSquared($this) > 1){ //Tolerate up to 1 block to avoid problems with client-sided physics when spawning in blocks - $this->sendPosition($this, null, null, MovePlayerPacket::MODE_RESET); + if($this->forceMoveSync !== null and $newPos->distanceSquared($this->forceMoveSync) > 1){ //Tolerate up to 1 block to avoid problems with client-sided physics when spawning in blocks $this->server->getLogger()->debug("Got outdated pre-teleport movement from " . $this->getName() . ", received " . $newPos . ", expected " . $this->asVector3()); //Still getting movements from before teleport, ignore them }elseif((!$this->isAlive() or !$this->spawned) and $newPos->distanceSquared($this) > 0.01){ @@ -2260,9 +2306,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->server->getLogger()->debug("Reverted movement of " . $this->getName() . " due to not alive or not spawned, received " . $newPos . ", locked at " . $this->asVector3()); }else{ // Once we get a movement within a reasonable distance, treat it as a teleport ACK and remove position lock - if($this->isTeleporting){ - $this->isTeleporting = false; - } + $this->forceMoveSync = null; $packet->yaw = fmod($packet->yaw, 360); $packet->pitch = fmod($packet->pitch, 360); @@ -2272,7 +2316,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } $this->setRotation($packet->yaw, $packet->pitch); - $this->newPosition = $newPos; + $this->handleMovement($newPos); } return true; @@ -2280,7 +2324,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ public function handleLevelSoundEvent(LevelSoundEventPacket $packet) : bool{ //TODO: add events so plugins can change this - $this->getLevel()->broadcastPacketToViewers($this, $packet); + $this->getLevelNonNull()->broadcastPacketToViewers($this, $packet); return true; } @@ -2737,7 +2781,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $item = $block->getPickedItem(); if($packet->addUserData){ - $tile = $this->getLevel()->getTile($block); + $tile = $this->getLevelNonNull()->getTile($block); if($tile instanceof Tile){ $nbt = $tile->getCleanedNBT(); if($nbt instanceof CompoundTag){ @@ -3269,7 +3313,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } /** - * Adds a title text to the user's screen, with an optional subtitle. + * @deprecated + * @see Player::sendTitle() * * @param int $fadeIn Duration in ticks for fade-in. If -1 is given, client-sided defaults will be used. * @param int $stay Duration in ticks to stay on screen for @@ -3278,28 +3323,55 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ * @return void */ public function addTitle(string $title, string $subtitle = "", int $fadeIn = -1, int $stay = -1, int $fadeOut = -1){ + $this->sendTitle($title, $subtitle, $fadeIn, $stay, $fadeOut); + } + + /** + * Adds a title text to the user's screen, with an optional subtitle. + * + * @param int $fadeIn Duration in ticks for fade-in. If -1 is given, client-sided defaults will be used. + * @param int $stay Duration in ticks to stay on screen for + * @param int $fadeOut Duration in ticks for fade-out. + */ + public function sendTitle(string $title, string $subtitle = "", int $fadeIn = -1, int $stay = -1, int $fadeOut = -1) : void{ $this->setTitleDuration($fadeIn, $stay, $fadeOut); if($subtitle !== ""){ - $this->addSubTitle($subtitle); + $this->sendSubTitle($subtitle); } $this->sendTitleText($title, SetTitlePacket::TYPE_SET_TITLE); } /** - * Sets the subtitle message, without sending a title. + * @deprecated + * @see Player::sendSubTitle() * * @return void */ public function addSubTitle(string $subtitle){ + $this->sendSubTitle($subtitle); + } + + /** + * Sets the subtitle message, without sending a title. + */ + public function sendSubTitle(string $subtitle) : void{ $this->sendTitleText($subtitle, SetTitlePacket::TYPE_SET_SUBTITLE); } /** - * Adds small text to the user's screen. + * @deprecated + * @see Player::sendActionBarMessage() * * @return void */ public function addActionBarMessage(string $message){ + $this->sendActionBarMessage($message); + } + + /** + * Adds small text to the user's screen. + */ + public function sendActionBarMessage(string $message) : void{ $this->sendTitleText($message, SetTitlePacket::TYPE_SET_ACTIONBAR_MESSAGE); } @@ -3598,7 +3670,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } if($this->hasValidSpawnPosition()){ - $this->namedtag->setString("SpawnLevel", $this->spawnPosition->getLevel()->getFolderName()); + $this->namedtag->setString("SpawnLevel", $this->spawnPosition->getLevelNonNull()->getFolderName()); $this->namedtag->setInt("SpawnX", $this->spawnPosition->getFloorX()); $this->namedtag->setInt("SpawnY", $this->spawnPosition->getFloorY()); $this->namedtag->setInt("SpawnZ", $this->spawnPosition->getFloorZ()); @@ -3642,7 +3714,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ //main inventory and drops the rest on the ground. $this->doCloseInventory(); - $ev = new PlayerDeathEvent($this, $this->getDrops()); + $ev = new PlayerDeathEvent($this, $this->getDrops(), null, $this->getXpDropAmount()); $ev->call(); if(!$ev->getKeepInventory()){ @@ -3659,8 +3731,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } } - //TODO: allow this number to be manipulated during PlayerDeathEvent - $this->level->dropExperience($this, $this->getXpDropAmount()); + $this->level->dropExperience($this, $ev->getXpDropAmount()); $this->setXpAndProgress(0, 0); if($ev->getDeathMessage() != ""){ @@ -3682,7 +3753,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $ev = new PlayerRespawnEvent($this, $this->getSpawn()); $ev->call(); - $realSpawn = Position::fromObject($ev->getRespawnPosition()->add(0.5, 0, 0.5), $ev->getRespawnPosition()->getLevel()); + $realSpawn = Position::fromObject($ev->getRespawnPosition()->add(0.5, 0, 0.5), $ev->getRespawnPosition()->getLevelNonNull()); $this->teleport($realSpawn); $this->setSprinting(false); @@ -3766,12 +3837,14 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $pk->onGround = $this->onGround; if($targets !== null){ + if(in_array($this, $targets, true)){ + $this->forceMoveSync = $pos->asVector3(); + } $this->server->broadcastPacket($targets, $pk); }else{ + $this->forceMoveSync = $pos->asVector3(); $this->dataPacket($pk); } - - $this->newPosition = null; } /** @@ -3789,11 +3862,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->resetFallDistance(); $this->nextChunkOrderRun = 0; - $this->newPosition = null; $this->stopSleep(); - $this->isTeleporting = true; - //TODO: workaround for player last pos not getting updated //Entity::updateMovement() normally handles this, but it's overridden with an empty function in Player $this->resetLastMovements(); diff --git a/src/pocketmine/PocketMine.php b/src/pocketmine/PocketMine.php index cc6d73343..312ec9869 100644 --- a/src/pocketmine/PocketMine.php +++ b/src/pocketmine/PocketMine.php @@ -25,6 +25,7 @@ namespace pocketmine { use pocketmine\utils\Git; use pocketmine\utils\MainLogger; + use pocketmine\utils\Process; use pocketmine\utils\ServerKiller; use pocketmine\utils\Terminal; use pocketmine\utils\Timezone; @@ -34,7 +35,7 @@ namespace pocketmine { require_once __DIR__ . '/VersionInfo.php'; - const MIN_PHP_VERSION = "7.2.0"; + const MIN_PHP_VERSION = "7.3.0"; /** * @param string $message @@ -279,7 +280,7 @@ namespace pocketmine { if(ThreadManager::getInstance()->stopAll() > 0){ $logger->debug("Some threads could not be stopped, performing a force-kill"); - Utils::kill(getmypid()); + Process::kill(getmypid()); } }while(false); diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index bbabc91ca..813d80aad 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -102,6 +102,7 @@ use pocketmine\updater\AutoUpdater; use pocketmine\utils\Config; use pocketmine\utils\Internet; use pocketmine\utils\MainLogger; +use pocketmine\utils\Process; use pocketmine\utils\Terminal; use pocketmine\utils\TextFormat; use pocketmine\utils\Utils; @@ -1665,7 +1666,7 @@ class Server{ } foreach($recipients as $recipient){ - $recipient->addTitle($title, $subtitle, $fadeIn, $stay, $fadeOut); + $recipient->sendTitle($title, $subtitle, $fadeIn, $stay, $fadeOut); } return count($recipients); @@ -1935,7 +1936,7 @@ class Server{ }catch(\Throwable $e){ $this->logger->logException($e); $this->logger->emergency("Crashed while crashing, killing process"); - @Utils::kill(getmypid()); + @Process::kill(getmypid()); } } @@ -2128,7 +2129,7 @@ class Server{ echo "--- Waiting $spacing seconds to throttle automatic restart (you can kill the process safely now) ---" . PHP_EOL; sleep($spacing); } - @Utils::kill(getmypid()); + @Process::kill(getmypid()); exit(1); } @@ -2329,10 +2330,10 @@ class Server{ private function titleTick() : void{ Timings::$titleTickTimer->startTiming(); - $d = Utils::getRealMemoryUsage(); + $d = Process::getRealMemoryUsage(); - $u = Utils::getMemoryUsage(true); - $usage = sprintf("%g/%g/%g/%g MB @ %d threads", round(($u[0] / 1024) / 1024, 2), round(($d[0] / 1024) / 1024, 2), round(($u[1] / 1024) / 1024, 2), round(($u[2] / 1024) / 1024, 2), Utils::getThreadCount()); + $u = Process::getAdvancedMemoryUsage(); + $usage = sprintf("%g/%g/%g/%g MB @ %d threads", round(($u[0] / 1024) / 1024, 2), round(($d[0] / 1024) / 1024, 2), round(($u[1] / 1024) / 1024, 2), round(($u[2] / 1024) / 1024, 2), Process::getThreadCount()); echo "\x1b]0;" . $this->getName() . " " . $this->getPocketMineVersion() . diff --git a/src/pocketmine/block/Anvil.php b/src/pocketmine/block/Anvil.php index 2eb8880af..7fa7cfc1a 100644 --- a/src/pocketmine/block/Anvil.php +++ b/src/pocketmine/block/Anvil.php @@ -110,6 +110,6 @@ class Anvil extends Fallable{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{ $direction = ($player !== null ? $player->getDirection() : 0) & 0x03; $this->meta = $this->getVariant() | $direction; - return $this->getLevel()->setBlock($blockReplace, $this, true, true); + return $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); } } diff --git a/src/pocketmine/block/BaseRail.php b/src/pocketmine/block/BaseRail.php index c5fb8b2e3..1cd8cf167 100644 --- a/src/pocketmine/block/BaseRail.php +++ b/src/pocketmine/block/BaseRail.php @@ -91,7 +91,7 @@ abstract class BaseRail extends Flowable{ } public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{ - if(!$blockReplace->getSide(Vector3::SIDE_DOWN)->isTransparent() and $this->getLevel()->setBlock($blockReplace, $this, true, true)){ + if(!$blockReplace->getSide(Vector3::SIDE_DOWN)->isTransparent() and $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true)){ $this->tryReconnect(); return true; } @@ -279,7 +279,7 @@ abstract class BaseRail extends Flowable{ isset(self::ASCENDING_SIDES[$this->meta & 0x07]) and $this->getSide(self::ASCENDING_SIDES[$this->meta & 0x07])->isTransparent() )){ - $this->getLevel()->useBreakOn($this); + $this->getLevelNonNull()->useBreakOn($this); } } diff --git a/src/pocketmine/block/Bed.php b/src/pocketmine/block/Bed.php index 047f7cc21..166b1c35b 100644 --- a/src/pocketmine/block/Bed.php +++ b/src/pocketmine/block/Bed.php @@ -83,7 +83,7 @@ class Bed extends Transparent{ $this->meta &= ~self::BITFLAG_OCCUPIED; } - $this->getLevel()->setBlock($this, $this, false, false); + $this->getLevelNonNull()->setBlock($this, $this, false, false); if(($other = $this->getOtherHalf()) !== null and $other->isOccupied() !== $occupied){ $other->setOccupied($occupied); @@ -137,7 +137,7 @@ class Bed extends Transparent{ return true; } - $time = $this->getLevel()->getTime() % Level::TIME_FULL; + $time = $this->getLevelNonNull()->getTimeOfDay(); $isNight = ($time >= Level::TIME_NIGHT and $time < Level::TIME_SUNRISE); @@ -168,11 +168,11 @@ class Bed extends Transparent{ $meta = (($player instanceof Player ? $player->getDirection() : 0) - 1) & 0x03; $next = $this->getSide(self::getOtherHalfSide($meta)); if($next->canBeReplaced() and !$next->getSide(Vector3::SIDE_DOWN)->isTransparent()){ - $this->getLevel()->setBlock($blockReplace, BlockFactory::get($this->id, $meta), true, true); - $this->getLevel()->setBlock($next, BlockFactory::get($this->id, $meta | self::BITFLAG_HEAD), true, true); + $this->getLevelNonNull()->setBlock($blockReplace, BlockFactory::get($this->id, $meta), true, true); + $this->getLevelNonNull()->setBlock($next, BlockFactory::get($this->id, $meta | self::BITFLAG_HEAD), true, true); - Tile::createTile(Tile::BED, $this->getLevel(), TileBed::createNBT($this, $face, $item, $player)); - Tile::createTile(Tile::BED, $this->getLevel(), TileBed::createNBT($next, $face, $item, $player)); + Tile::createTile(Tile::BED, $this->getLevelNonNull(), TileBed::createNBT($this, $face, $item, $player)); + Tile::createTile(Tile::BED, $this->getLevelNonNull(), TileBed::createNBT($next, $face, $item, $player)); return true; } @@ -194,7 +194,7 @@ class Bed extends Transparent{ } private function getItem() : Item{ - $tile = $this->getLevel()->getTile($this); + $tile = $this->getLevelNonNull()->getTile($this); if($tile instanceof TileBed){ return ItemFactory::get($this->getItemId(), $tile->getColor()); } diff --git a/src/pocketmine/block/Block.php b/src/pocketmine/block/Block.php index df18785e1..f5583753e 100644 --- a/src/pocketmine/block/Block.php +++ b/src/pocketmine/block/Block.php @@ -154,7 +154,7 @@ class Block extends Position implements BlockIds, Metadatable{ * Places the Block, using block space and block target, and side. Returns if the block has been placed. */ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{ - return $this->getLevel()->setBlock($this, $this, true, true); + return $this->getLevelNonNull()->setBlock($this, $this, true, true); } /** @@ -204,7 +204,7 @@ class Block extends Position implements BlockIds, Metadatable{ * Do the actions needed so the block is broken with the Item */ public function onBreak(Item $item, Player $player = null) : bool{ - return $this->getLevel()->setBlock($this, BlockFactory::get(Block::AIR), true, true); + return $this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::AIR), true, true); } /** @@ -479,7 +479,7 @@ class Block extends Position implements BlockIds, Metadatable{ */ public function getSide(int $side, int $step = 1){ if($this->isValid()){ - return $this->getLevel()->getBlock(Vector3::getSide($side, $step)); + return $this->getLevelNonNull()->getBlock(Vector3::getSide($side, $step)); } return BlockFactory::get(Block::AIR, 0, Position::fromObject(Vector3::getSide($side, $step))); diff --git a/src/pocketmine/block/BoneBlock.php b/src/pocketmine/block/BoneBlock.php index 696565fae..364e8332b 100644 --- a/src/pocketmine/block/BoneBlock.php +++ b/src/pocketmine/block/BoneBlock.php @@ -55,7 +55,7 @@ class BoneBlock extends Solid{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{ $this->meta = PillarRotationHelper::getMetaFromFace($this->meta, $face); - return $this->getLevel()->setBlock($blockReplace, $this, true, true); + return $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); } public function getVariantBitmask() : int{ diff --git a/src/pocketmine/block/BurningFurnace.php b/src/pocketmine/block/BurningFurnace.php index c189f5b65..fdb61536c 100644 --- a/src/pocketmine/block/BurningFurnace.php +++ b/src/pocketmine/block/BurningFurnace.php @@ -68,18 +68,18 @@ class BurningFurnace extends Solid{ 3 => 3 ]; $this->meta = $faces[$player instanceof Player ? $player->getDirection() : 0]; - $this->getLevel()->setBlock($blockReplace, $this, true, true); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); - Tile::createTile(Tile::FURNACE, $this->getLevel(), TileFurnace::createNBT($this, $face, $item, $player)); + Tile::createTile(Tile::FURNACE, $this->getLevelNonNull(), TileFurnace::createNBT($this, $face, $item, $player)); return true; } public function onActivate(Item $item, Player $player = null) : bool{ if($player instanceof Player){ - $furnace = $this->getLevel()->getTile($this); + $furnace = $this->getLevelNonNull()->getTile($this); if(!($furnace instanceof TileFurnace)){ - $furnace = Tile::createTile(Tile::FURNACE, $this->getLevel(), TileFurnace::createNBT($this)); + $furnace = Tile::createTile(Tile::FURNACE, $this->getLevelNonNull(), TileFurnace::createNBT($this)); if(!($furnace instanceof TileFurnace)){ return true; } diff --git a/src/pocketmine/block/Cactus.php b/src/pocketmine/block/Cactus.php index 30effe652..2fd5fbff7 100644 --- a/src/pocketmine/block/Cactus.php +++ b/src/pocketmine/block/Cactus.php @@ -72,12 +72,12 @@ class Cactus extends Transparent{ public function onNearbyBlockChange() : void{ $down = $this->getSide(Vector3::SIDE_DOWN); if($down->getId() !== self::SAND and $down->getId() !== self::CACTUS){ - $this->getLevel()->useBreakOn($this); + $this->getLevelNonNull()->useBreakOn($this); }else{ for($side = 2; $side <= 5; ++$side){ $b = $this->getSide($side); if($b->isSolid()){ - $this->getLevel()->useBreakOn($this); + $this->getLevelNonNull()->useBreakOn($this); break; } } @@ -92,23 +92,23 @@ class Cactus extends Transparent{ if($this->getSide(Vector3::SIDE_DOWN)->getId() !== self::CACTUS){ if($this->meta === 0x0f){ for($y = 1; $y < 3; ++$y){ - $b = $this->getLevel()->getBlockAt($this->x, $this->y + $y, $this->z); + $b = $this->getLevelNonNull()->getBlockAt($this->x, $this->y + $y, $this->z); if($b->getId() === self::AIR){ $ev = new BlockGrowEvent($b, BlockFactory::get(Block::CACTUS)); $ev->call(); if($ev->isCancelled()){ break; } - $this->getLevel()->setBlock($b, $ev->getNewState(), true); + $this->getLevelNonNull()->setBlock($b, $ev->getNewState(), true); }else{ break; } } $this->meta = 0; - $this->getLevel()->setBlock($this, $this); + $this->getLevelNonNull()->setBlock($this, $this); }else{ ++$this->meta; - $this->getLevel()->setBlock($this, $this); + $this->getLevelNonNull()->setBlock($this, $this); } } } @@ -121,7 +121,7 @@ class Cactus extends Transparent{ $block2 = $this->getSide(Vector3::SIDE_WEST); $block3 = $this->getSide(Vector3::SIDE_EAST); if(!$block0->isSolid() and !$block1->isSolid() and !$block2->isSolid() and !$block3->isSolid()){ - $this->getLevel()->setBlock($this, $this, true); + $this->getLevelNonNull()->setBlock($this, $this, true); return true; } diff --git a/src/pocketmine/block/Cake.php b/src/pocketmine/block/Cake.php index 9ac80f6a7..8cb3165ec 100644 --- a/src/pocketmine/block/Cake.php +++ b/src/pocketmine/block/Cake.php @@ -66,7 +66,7 @@ class Cake extends Transparent implements FoodSource{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{ $down = $this->getSide(Vector3::SIDE_DOWN); if($down->getId() !== self::AIR){ - $this->getLevel()->setBlock($blockReplace, $this, true, true); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); return true; } @@ -76,7 +76,7 @@ class Cake extends Transparent implements FoodSource{ public function onNearbyBlockChange() : void{ if($this->getSide(Vector3::SIDE_DOWN)->getId() === self::AIR){ //Replace with common break method - $this->getLevel()->setBlock($this, BlockFactory::get(Block::AIR), true); + $this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::AIR), true); } } diff --git a/src/pocketmine/block/Carpet.php b/src/pocketmine/block/Carpet.php index 225f9db09..bd700f8d8 100644 --- a/src/pocketmine/block/Carpet.php +++ b/src/pocketmine/block/Carpet.php @@ -64,7 +64,7 @@ class Carpet extends Flowable{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{ $down = $this->getSide(Vector3::SIDE_DOWN); if($down->getId() !== self::AIR){ - $this->getLevel()->setBlock($blockReplace, $this, true, true); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); return true; } @@ -74,7 +74,7 @@ class Carpet extends Flowable{ public function onNearbyBlockChange() : void{ if($this->getSide(Vector3::SIDE_DOWN)->getId() === self::AIR){ - $this->getLevel()->useBreakOn($this); + $this->getLevelNonNull()->useBreakOn($this); } } diff --git a/src/pocketmine/block/Chest.php b/src/pocketmine/block/Chest.php index 47d96e33f..af2b8e498 100644 --- a/src/pocketmine/block/Chest.php +++ b/src/pocketmine/block/Chest.php @@ -81,7 +81,7 @@ class Chest extends Transparent{ } $c = $this->getSide($side); if($c->getId() === $this->id and $c->getDamage() === $this->meta){ - $tile = $this->getLevel()->getTile($c); + $tile = $this->getLevelNonNull()->getTile($c); if($tile instanceof TileChest and !$tile->isPaired()){ $chest = $tile; break; @@ -89,8 +89,8 @@ class Chest extends Transparent{ } } - $this->getLevel()->setBlock($blockReplace, $this, true, true); - $tile = Tile::createTile(Tile::CHEST, $this->getLevel(), TileChest::createNBT($this, $face, $item, $player)); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); + $tile = Tile::createTile(Tile::CHEST, $this->getLevelNonNull(), TileChest::createNBT($this, $face, $item, $player)); if($chest instanceof TileChest and $tile instanceof TileChest){ $chest->pairWith($tile); @@ -103,12 +103,12 @@ class Chest extends Transparent{ public function onActivate(Item $item, Player $player = null) : bool{ if($player instanceof Player){ - $t = $this->getLevel()->getTile($this); + $t = $this->getLevelNonNull()->getTile($this); $chest = null; if($t instanceof TileChest){ $chest = $t; }else{ - $chest = Tile::createTile(Tile::CHEST, $this->getLevel(), TileChest::createNBT($this)); + $chest = Tile::createTile(Tile::CHEST, $this->getLevelNonNull(), TileChest::createNBT($this)); if(!($chest instanceof TileChest)){ return true; } diff --git a/src/pocketmine/block/Crops.php b/src/pocketmine/block/Crops.php index eaebfe0a2..81afd0c17 100644 --- a/src/pocketmine/block/Crops.php +++ b/src/pocketmine/block/Crops.php @@ -33,7 +33,7 @@ abstract class Crops extends Flowable{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{ if($blockReplace->getSide(Vector3::SIDE_DOWN)->getId() === Block::FARMLAND){ - $this->getLevel()->setBlock($blockReplace, $this, true, true); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); return true; } @@ -52,7 +52,7 @@ abstract class Crops extends Flowable{ $ev = new BlockGrowEvent($this, $block); $ev->call(); if(!$ev->isCancelled()){ - $this->getLevel()->setBlock($this, $ev->getNewState(), true, true); + $this->getLevelNonNull()->setBlock($this, $ev->getNewState(), true, true); } $item->pop(); @@ -65,7 +65,7 @@ abstract class Crops extends Flowable{ public function onNearbyBlockChange() : void{ if($this->getSide(Vector3::SIDE_DOWN)->getId() !== Block::FARMLAND){ - $this->getLevel()->useBreakOn($this); + $this->getLevelNonNull()->useBreakOn($this); } } @@ -81,7 +81,7 @@ abstract class Crops extends Flowable{ $ev = new BlockGrowEvent($this, $block); $ev->call(); if(!$ev->isCancelled()){ - $this->getLevel()->setBlock($this, $ev->getNewState(), true, true); + $this->getLevelNonNull()->setBlock($this, $ev->getNewState(), true, true); } } } diff --git a/src/pocketmine/block/Dandelion.php b/src/pocketmine/block/Dandelion.php index 3071df931..6ffb28a7f 100644 --- a/src/pocketmine/block/Dandelion.php +++ b/src/pocketmine/block/Dandelion.php @@ -42,7 +42,7 @@ class Dandelion extends Flowable{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{ $down = $this->getSide(Vector3::SIDE_DOWN); if($down->getId() === Block::GRASS or $down->getId() === Block::DIRT or $down->getId() === Block::FARMLAND){ - $this->getLevel()->setBlock($blockReplace, $this, true, true); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); return true; } @@ -52,7 +52,7 @@ class Dandelion extends Flowable{ public function onNearbyBlockChange() : void{ if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){ - $this->getLevel()->useBreakOn($this); + $this->getLevelNonNull()->useBreakOn($this); } } diff --git a/src/pocketmine/block/DeadBush.php b/src/pocketmine/block/DeadBush.php index c42abcd9f..8ee1b0977 100644 --- a/src/pocketmine/block/DeadBush.php +++ b/src/pocketmine/block/DeadBush.php @@ -51,7 +51,7 @@ class DeadBush extends Flowable{ public function onNearbyBlockChange() : void{ if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){ - $this->getLevel()->useBreakOn($this); + $this->getLevelNonNull()->useBreakOn($this); } } diff --git a/src/pocketmine/block/Dirt.php b/src/pocketmine/block/Dirt.php index 96f8da27c..9b00df1e5 100644 --- a/src/pocketmine/block/Dirt.php +++ b/src/pocketmine/block/Dirt.php @@ -54,9 +54,9 @@ class Dirt extends Solid{ if($item instanceof Hoe){ $item->applyDamage(1); if($this->meta === 1){ - $this->getLevel()->setBlock($this, BlockFactory::get(Block::DIRT), true); + $this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::DIRT), true); }else{ - $this->getLevel()->setBlock($this, BlockFactory::get(Block::FARMLAND), true); + $this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::FARMLAND), true); } return true; diff --git a/src/pocketmine/block/Door.php b/src/pocketmine/block/Door.php index d100a4c70..d4de03dc4 100644 --- a/src/pocketmine/block/Door.php +++ b/src/pocketmine/block/Door.php @@ -201,9 +201,9 @@ abstract class Door extends Transparent{ public function onNearbyBlockChange() : void{ if($this->getSide(Vector3::SIDE_DOWN)->getId() === self::AIR){ //Replace with common break method - $this->getLevel()->setBlock($this, BlockFactory::get(Block::AIR), false); + $this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::AIR), false); if($this->getSide(Vector3::SIDE_UP) instanceof Door){ - $this->getLevel()->setBlock($this->getSide(Vector3::SIDE_UP), BlockFactory::get(Block::AIR), false); + $this->getLevelNonNull()->setBlock($this->getSide(Vector3::SIDE_UP), BlockFactory::get(Block::AIR), false); } } } @@ -230,8 +230,8 @@ abstract class Door extends Transparent{ } $this->setDamage($player->getDirection() & 0x03); - $this->getLevel()->setBlock($blockReplace, $this, true, true); //Bottom - $this->getLevel()->setBlock($blockUp, BlockFactory::get($this->getId(), $metaUp), true); //Top + $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); //Bottom + $this->getLevelNonNull()->setBlock($blockUp, BlockFactory::get($this->getId(), $metaUp), true); //Top return true; } diff --git a/src/pocketmine/block/DoublePlant.php b/src/pocketmine/block/DoublePlant.php index 57b1c43b9..c27b53ef1 100644 --- a/src/pocketmine/block/DoublePlant.php +++ b/src/pocketmine/block/DoublePlant.php @@ -57,8 +57,8 @@ class DoublePlant extends Flowable{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{ $id = $blockReplace->getSide(Vector3::SIDE_DOWN)->getId(); if(($id === Block::GRASS or $id === Block::DIRT) and $blockReplace->getSide(Vector3::SIDE_UP)->canBeReplaced()){ - $this->getLevel()->setBlock($blockReplace, $this, false, false); - $this->getLevel()->setBlock($blockReplace->getSide(Vector3::SIDE_UP), BlockFactory::get($this->id, $this->meta | self::BITFLAG_TOP), false, false); + $this->getLevelNonNull()->setBlock($blockReplace, $this, false, false); + $this->getLevelNonNull()->setBlock($blockReplace->getSide(Vector3::SIDE_UP), BlockFactory::get($this->id, $this->meta | self::BITFLAG_TOP), false, false); return true; } @@ -85,7 +85,7 @@ class DoublePlant extends Flowable{ public function onNearbyBlockChange() : void{ if(!$this->isValidHalfPlant() or (($this->meta & self::BITFLAG_TOP) === 0 and $this->getSide(Vector3::SIDE_DOWN)->isTransparent())){ - $this->getLevel()->useBreakOn($this); + $this->getLevelNonNull()->useBreakOn($this); } } diff --git a/src/pocketmine/block/EnchantingTable.php b/src/pocketmine/block/EnchantingTable.php index 5574677c4..2724ff51e 100644 --- a/src/pocketmine/block/EnchantingTable.php +++ b/src/pocketmine/block/EnchantingTable.php @@ -40,9 +40,9 @@ class EnchantingTable extends Transparent{ } public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{ - $this->getLevel()->setBlock($blockReplace, $this, true, true); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); - Tile::createTile(Tile::ENCHANT_TABLE, $this->getLevel(), TileEnchantTable::createNBT($this, $face, $item, $player)); + Tile::createTile(Tile::ENCHANT_TABLE, $this->getLevelNonNull(), TileEnchantTable::createNBT($this, $face, $item, $player)); return true; } diff --git a/src/pocketmine/block/EnderChest.php b/src/pocketmine/block/EnderChest.php index 1f1f5e6dd..00438b472 100644 --- a/src/pocketmine/block/EnderChest.php +++ b/src/pocketmine/block/EnderChest.php @@ -69,8 +69,8 @@ class EnderChest extends Chest{ $this->meta = $faces[$player instanceof Player ? $player->getDirection() : 0]; - $this->getLevel()->setBlock($blockReplace, $this, true, true); - Tile::createTile(Tile::ENDER_CHEST, $this->getLevel(), TileEnderChest::createNBT($this, $face, $item, $player)); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); + Tile::createTile(Tile::ENDER_CHEST, $this->getLevelNonNull(), TileEnderChest::createNBT($this, $face, $item, $player)); return true; } @@ -78,12 +78,12 @@ class EnderChest extends Chest{ public function onActivate(Item $item, Player $player = null) : bool{ if($player instanceof Player){ - $t = $this->getLevel()->getTile($this); + $t = $this->getLevelNonNull()->getTile($this); $enderChest = null; if($t instanceof TileEnderChest){ $enderChest = $t; }else{ - $enderChest = Tile::createTile(Tile::ENDER_CHEST, $this->getLevel(), TileEnderChest::createNBT($this)); + $enderChest = Tile::createTile(Tile::ENDER_CHEST, $this->getLevelNonNull(), TileEnderChest::createNBT($this)); if(!($enderChest instanceof TileEnderChest)){ return true; } diff --git a/src/pocketmine/block/Fallable.php b/src/pocketmine/block/Fallable.php index 10cba9554..15d87b97d 100644 --- a/src/pocketmine/block/Fallable.php +++ b/src/pocketmine/block/Fallable.php @@ -37,7 +37,7 @@ abstract class Fallable extends Solid{ $nbt->setInt("TileID", $this->getId()); $nbt->setByte("Data", $this->getDamage()); - $fall = Entity::createEntity("FallingSand", $this->getLevel(), $nbt); + $fall = Entity::createEntity("FallingSand", $this->getLevelNonNull(), $nbt); if($fall !== null){ $fall->spawnToAll(); diff --git a/src/pocketmine/block/FenceGate.php b/src/pocketmine/block/FenceGate.php index 5f5a4d391..66b135f70 100644 --- a/src/pocketmine/block/FenceGate.php +++ b/src/pocketmine/block/FenceGate.php @@ -69,7 +69,7 @@ class FenceGate extends Transparent{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{ $this->meta = ($player instanceof Player ? ($player->getDirection() - 1) & 0x03 : 0); - $this->getLevel()->setBlock($blockReplace, $this, true, true); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); return true; } @@ -85,7 +85,7 @@ class FenceGate extends Transparent{ $this->meta |= (($player->getDirection() - 1) & 0x02); } - $this->getLevel()->setBlock($this, $this, true); + $this->getLevelNonNull()->setBlock($this, $this, true); $this->level->addSound(new DoorSound($this)); return true; } diff --git a/src/pocketmine/block/Fire.php b/src/pocketmine/block/Fire.php index f89b2f887..832ab6194 100644 --- a/src/pocketmine/block/Fire.php +++ b/src/pocketmine/block/Fire.php @@ -82,7 +82,7 @@ class Fire extends Flowable{ public function onNearbyBlockChange() : void{ if(!$this->getSide(Vector3::SIDE_DOWN)->isSolid() and !$this->hasAdjacentFlammableBlocks()){ - $this->getLevel()->setBlock($this, BlockFactory::get(Block::AIR), true); + $this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::AIR), true); }else{ $this->level->scheduleDelayedBlockUpdate($this, mt_rand(30, 40)); } diff --git a/src/pocketmine/block/Flower.php b/src/pocketmine/block/Flower.php index 4bb3ee7ce..7a1b5baf9 100644 --- a/src/pocketmine/block/Flower.php +++ b/src/pocketmine/block/Flower.php @@ -62,7 +62,7 @@ class Flower extends Flowable{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{ $down = $this->getSide(Vector3::SIDE_DOWN); if($down->getId() === Block::GRASS or $down->getId() === Block::DIRT or $down->getId() === Block::FARMLAND){ - $this->getLevel()->setBlock($blockReplace, $this, true); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true); return true; } @@ -72,7 +72,7 @@ class Flower extends Flowable{ public function onNearbyBlockChange() : void{ if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){ - $this->getLevel()->useBreakOn($this); + $this->getLevelNonNull()->useBreakOn($this); } } diff --git a/src/pocketmine/block/FlowerPot.php b/src/pocketmine/block/FlowerPot.php index cc5d3b9ed..5e1a41d48 100644 --- a/src/pocketmine/block/FlowerPot.php +++ b/src/pocketmine/block/FlowerPot.php @@ -62,19 +62,19 @@ class FlowerPot extends Flowable{ return false; } - $this->getLevel()->setBlock($blockReplace, $this, true, true); - Tile::createTile(Tile::FLOWER_POT, $this->getLevel(), TileFlowerPot::createNBT($this, $face, $item, $player)); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); + Tile::createTile(Tile::FLOWER_POT, $this->getLevelNonNull(), TileFlowerPot::createNBT($this, $face, $item, $player)); return true; } public function onNearbyBlockChange() : void{ if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){ - $this->getLevel()->useBreakOn($this); + $this->getLevelNonNull()->useBreakOn($this); } } public function onActivate(Item $item, Player $player = null) : bool{ - $pot = $this->getLevel()->getTile($this); + $pot = $this->getLevelNonNull()->getTile($this); if(!($pot instanceof TileFlowerPot)){ return false; } @@ -83,7 +83,7 @@ class FlowerPot extends Flowable{ } $this->setDamage(self::STATE_FULL); //specific damage value is unnecessary, it just needs to be non-zero to show an item. - $this->getLevel()->setBlock($this, $this, true, false); + $this->getLevelNonNull()->setBlock($this, $this, true, false); $pot->setItem($item->pop()); return true; @@ -96,7 +96,7 @@ class FlowerPot extends Flowable{ public function getDropsForCompatibleTool(Item $item) : array{ $items = parent::getDropsForCompatibleTool($item); - $tile = $this->getLevel()->getTile($this); + $tile = $this->getLevelNonNull()->getTile($this); if($tile instanceof TileFlowerPot){ $item = $tile->getItem(); if($item->getId() !== Item::AIR){ diff --git a/src/pocketmine/block/GlazedTerracotta.php b/src/pocketmine/block/GlazedTerracotta.php index 9e3862666..e2ae343bf 100644 --- a/src/pocketmine/block/GlazedTerracotta.php +++ b/src/pocketmine/block/GlazedTerracotta.php @@ -53,7 +53,7 @@ class GlazedTerracotta extends Solid{ $this->meta = $faces[(~($player->getDirection() - 1)) & 0x03]; } - return $this->getLevel()->setBlock($blockReplace, $this, true, true); + return $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); } public function getVariantBitmask() : int{ diff --git a/src/pocketmine/block/GlowingRedstoneOre.php b/src/pocketmine/block/GlowingRedstoneOre.php index 0825bf546..94a3ce8ad 100644 --- a/src/pocketmine/block/GlowingRedstoneOre.php +++ b/src/pocketmine/block/GlowingRedstoneOre.php @@ -53,6 +53,6 @@ class GlowingRedstoneOre extends RedstoneOre{ } public function onRandomTick() : void{ - $this->getLevel()->setBlock($this, BlockFactory::get(Block::REDSTONE_ORE, $this->meta), false, false); + $this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::REDSTONE_ORE, $this->meta), false, false); } } diff --git a/src/pocketmine/block/Grass.php b/src/pocketmine/block/Grass.php index afd4b54ad..8313cc78c 100644 --- a/src/pocketmine/block/Grass.php +++ b/src/pocketmine/block/Grass.php @@ -100,17 +100,17 @@ class Grass extends Solid{ public function onActivate(Item $item, Player $player = null) : bool{ if($item->getId() === Item::DYE and $item->getDamage() === 0x0F){ $item->pop(); - TallGrassObject::growGrass($this->getLevel(), $this, new Random(mt_rand()), 8, 2); + TallGrassObject::growGrass($this->getLevelNonNull(), $this, new Random(mt_rand()), 8, 2); return true; }elseif($item instanceof Hoe){ $item->applyDamage(1); - $this->getLevel()->setBlock($this, BlockFactory::get(Block::FARMLAND)); + $this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::FARMLAND)); return true; }elseif($item instanceof Shovel and $this->getSide(Vector3::SIDE_UP)->getId() === Block::AIR){ $item->applyDamage(1); - $this->getLevel()->setBlock($this, BlockFactory::get(Block::GRASS_PATH)); + $this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::GRASS_PATH)); return true; } diff --git a/src/pocketmine/block/HayBale.php b/src/pocketmine/block/HayBale.php index 7dd2989da..a97c769ae 100644 --- a/src/pocketmine/block/HayBale.php +++ b/src/pocketmine/block/HayBale.php @@ -46,7 +46,7 @@ class HayBale extends Solid{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{ $this->meta = PillarRotationHelper::getMetaFromFace($this->meta, $face); - $this->getLevel()->setBlock($blockReplace, $this, true, true); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); return true; } diff --git a/src/pocketmine/block/Ice.php b/src/pocketmine/block/Ice.php index 3c214cf3b..3e9953b50 100644 --- a/src/pocketmine/block/Ice.php +++ b/src/pocketmine/block/Ice.php @@ -57,7 +57,7 @@ class Ice extends Transparent{ public function onBreak(Item $item, Player $player = null) : bool{ if(($player === null or $player->isSurvival()) and !$item->hasEnchantment(Enchantment::SILK_TOUCH)){ - return $this->getLevel()->setBlock($this, BlockFactory::get(Block::WATER), true); + return $this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::WATER), true); } return parent::onBreak($item, $player); } diff --git a/src/pocketmine/block/ItemFrame.php b/src/pocketmine/block/ItemFrame.php index 6644c682b..cdb3ca89a 100644 --- a/src/pocketmine/block/ItemFrame.php +++ b/src/pocketmine/block/ItemFrame.php @@ -46,7 +46,7 @@ class ItemFrame extends Flowable{ public function onActivate(Item $item, Player $player = null) : bool{ $tile = $this->level->getTile($this); if(!($tile instanceof TileItemFrame)){ - $tile = Tile::createTile(Tile::ITEM_FRAME, $this->getLevel(), TileItemFrame::createNBT($this)); + $tile = Tile::createTile(Tile::ITEM_FRAME, $this->getLevelNonNull(), TileItemFrame::createNBT($this)); if(!($tile instanceof TileItemFrame)){ return true; } @@ -88,7 +88,7 @@ class ItemFrame extends Flowable{ $this->meta = $faces[$face]; $this->level->setBlock($blockReplace, $this, true, true); - Tile::createTile(Tile::ITEM_FRAME, $this->getLevel(), TileItemFrame::createNBT($this, $face, $item, $player)); + Tile::createTile(Tile::ITEM_FRAME, $this->getLevelNonNull(), TileItemFrame::createNBT($this, $face, $item, $player)); return true; diff --git a/src/pocketmine/block/Ladder.php b/src/pocketmine/block/Ladder.php index d2aab6a85..2de51c5ad 100644 --- a/src/pocketmine/block/Ladder.php +++ b/src/pocketmine/block/Ladder.php @@ -100,7 +100,7 @@ class Ladder extends Transparent{ ]; if(isset($faces[$face])){ $this->meta = $faces[$face]; - $this->getLevel()->setBlock($blockReplace, $this, true, true); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); return true; } diff --git a/src/pocketmine/block/Lava.php b/src/pocketmine/block/Lava.php index aa111d28a..086fd6666 100644 --- a/src/pocketmine/block/Lava.php +++ b/src/pocketmine/block/Lava.php @@ -115,8 +115,8 @@ class Lava extends Liquid{ } public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{ - $ret = $this->getLevel()->setBlock($this, $this, true, false); - $this->getLevel()->scheduleDelayedBlockUpdate($this, $this->tickRate()); + $ret = $this->getLevelNonNull()->setBlock($this, $this, true, false); + $this->getLevelNonNull()->scheduleDelayedBlockUpdate($this, $this->tickRate()); return $ret; } diff --git a/src/pocketmine/block/Leaves.php b/src/pocketmine/block/Leaves.php index 788e4a781..6eeb83e49 100644 --- a/src/pocketmine/block/Leaves.php +++ b/src/pocketmine/block/Leaves.php @@ -139,7 +139,7 @@ class Leaves extends Transparent{ public function onNearbyBlockChange() : void{ if(($this->meta & 0b00001100) === 0){ $this->meta |= 0x08; - $this->getLevel()->setBlock($this, $this, true, false); + $this->getLevelNonNull()->setBlock($this, $this, true, false); } } @@ -155,16 +155,16 @@ class Leaves extends Transparent{ $ev = new LeavesDecayEvent($this); $ev->call(); if($ev->isCancelled() or $this->findLog($this, $visited, 0)){ - $this->getLevel()->setBlock($this, $this, false, false); + $this->getLevelNonNull()->setBlock($this, $this, false, false); }else{ - $this->getLevel()->useBreakOn($this); + $this->getLevelNonNull()->useBreakOn($this); } } } public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{ $this->meta |= 0x04; - return $this->getLevel()->setBlock($this, $this, true); + return $this->getLevelNonNull()->setBlock($this, $this, true); } public function getVariantBitmask() : int{ diff --git a/src/pocketmine/block/MelonStem.php b/src/pocketmine/block/MelonStem.php index d8bf91caa..420059529 100644 --- a/src/pocketmine/block/MelonStem.php +++ b/src/pocketmine/block/MelonStem.php @@ -49,7 +49,7 @@ class MelonStem extends Crops{ $ev = new BlockGrowEvent($this, $block); $ev->call(); if(!$ev->isCancelled()){ - $this->getLevel()->setBlock($this, $ev->getNewState(), true); + $this->getLevelNonNull()->setBlock($this, $ev->getNewState(), true); } }else{ for($side = 2; $side <= 5; ++$side){ @@ -64,7 +64,7 @@ class MelonStem extends Crops{ $ev = new BlockGrowEvent($side, BlockFactory::get(Block::MELON_BLOCK)); $ev->call(); if(!$ev->isCancelled()){ - $this->getLevel()->setBlock($side, $ev->getNewState(), true); + $this->getLevelNonNull()->setBlock($side, $ev->getNewState(), true); } } } diff --git a/src/pocketmine/block/Mycelium.php b/src/pocketmine/block/Mycelium.php index edb42d0d1..26b1ea366 100644 --- a/src/pocketmine/block/Mycelium.php +++ b/src/pocketmine/block/Mycelium.php @@ -64,13 +64,13 @@ class Mycelium extends Solid{ $x = mt_rand($this->x - 1, $this->x + 1); $y = mt_rand($this->y - 2, $this->y + 2); $z = mt_rand($this->z - 1, $this->z + 1); - $block = $this->getLevel()->getBlockAt($x, $y, $z); + $block = $this->getLevelNonNull()->getBlockAt($x, $y, $z); if($block->getId() === Block::DIRT){ if($block->getSide(Vector3::SIDE_UP) instanceof Transparent){ $ev = new BlockSpreadEvent($block, $this, BlockFactory::get(Block::MYCELIUM)); $ev->call(); if(!$ev->isCancelled()){ - $this->getLevel()->setBlock($block, $ev->getNewState()); + $this->getLevelNonNull()->setBlock($block, $ev->getNewState()); } } } diff --git a/src/pocketmine/block/NetherWartPlant.php b/src/pocketmine/block/NetherWartPlant.php index 7428730ac..3ab129b57 100644 --- a/src/pocketmine/block/NetherWartPlant.php +++ b/src/pocketmine/block/NetherWartPlant.php @@ -46,7 +46,7 @@ class NetherWartPlant extends Flowable{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{ $down = $this->getSide(Vector3::SIDE_DOWN); if($down->getId() === Block::SOUL_SAND){ - $this->getLevel()->setBlock($blockReplace, $this, false, true); + $this->getLevelNonNull()->setBlock($blockReplace, $this, false, true); return true; } @@ -56,7 +56,7 @@ class NetherWartPlant extends Flowable{ public function onNearbyBlockChange() : void{ if($this->getSide(Vector3::SIDE_DOWN)->getId() !== Block::SOUL_SAND){ - $this->getLevel()->useBreakOn($this); + $this->getLevelNonNull()->useBreakOn($this); } } @@ -71,7 +71,7 @@ class NetherWartPlant extends Flowable{ $ev = new BlockGrowEvent($this, $block); $ev->call(); if(!$ev->isCancelled()){ - $this->getLevel()->setBlock($this, $ev->getNewState(), false, true); + $this->getLevelNonNull()->setBlock($this, $ev->getNewState(), false, true); } } } diff --git a/src/pocketmine/block/Pumpkin.php b/src/pocketmine/block/Pumpkin.php index 811dd06e5..e5ce7e4b2 100644 --- a/src/pocketmine/block/Pumpkin.php +++ b/src/pocketmine/block/Pumpkin.php @@ -51,7 +51,7 @@ class Pumpkin extends Solid{ if($player instanceof Player){ $this->meta = ((int) $player->getDirection() + 1) % 4; } - $this->getLevel()->setBlock($blockReplace, $this, true, true); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); return true; } diff --git a/src/pocketmine/block/PumpkinStem.php b/src/pocketmine/block/PumpkinStem.php index 8a6ca57b5..aa06ac367 100644 --- a/src/pocketmine/block/PumpkinStem.php +++ b/src/pocketmine/block/PumpkinStem.php @@ -49,7 +49,7 @@ class PumpkinStem extends Crops{ $ev = new BlockGrowEvent($this, $block); $ev->call(); if(!$ev->isCancelled()){ - $this->getLevel()->setBlock($this, $ev->getNewState(), true); + $this->getLevelNonNull()->setBlock($this, $ev->getNewState(), true); } }else{ for($side = 2; $side <= 5; ++$side){ @@ -64,7 +64,7 @@ class PumpkinStem extends Crops{ $ev = new BlockGrowEvent($side, BlockFactory::get(Block::PUMPKIN)); $ev->call(); if(!$ev->isCancelled()){ - $this->getLevel()->setBlock($side, $ev->getNewState(), true); + $this->getLevelNonNull()->setBlock($side, $ev->getNewState(), true); } } } diff --git a/src/pocketmine/block/Quartz.php b/src/pocketmine/block/Quartz.php index d21c62ecf..bfaaea46f 100644 --- a/src/pocketmine/block/Quartz.php +++ b/src/pocketmine/block/Quartz.php @@ -58,7 +58,7 @@ class Quartz extends Solid{ if($this->getVariant() !== self::NORMAL){ $this->meta = PillarRotationHelper::getMetaFromFace($this->meta, $face); } - return $this->getLevel()->setBlock($blockReplace, $this, true, true); + return $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); } public function getToolType() : int{ diff --git a/src/pocketmine/block/RedMushroom.php b/src/pocketmine/block/RedMushroom.php index 5c4d5296b..d9d48b62a 100644 --- a/src/pocketmine/block/RedMushroom.php +++ b/src/pocketmine/block/RedMushroom.php @@ -45,14 +45,14 @@ class RedMushroom extends Flowable{ public function onNearbyBlockChange() : void{ if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){ - $this->getLevel()->useBreakOn($this); + $this->getLevelNonNull()->useBreakOn($this); } } public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{ $down = $this->getSide(Vector3::SIDE_DOWN); if(!$down->isTransparent()){ - $this->getLevel()->setBlock($blockReplace, $this, true, true); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); return true; } diff --git a/src/pocketmine/block/RedstoneOre.php b/src/pocketmine/block/RedstoneOre.php index 0557d0be9..a9fab687c 100644 --- a/src/pocketmine/block/RedstoneOre.php +++ b/src/pocketmine/block/RedstoneOre.php @@ -47,16 +47,16 @@ class RedstoneOre extends Solid{ } public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{ - return $this->getLevel()->setBlock($this, $this, true, false); + return $this->getLevelNonNull()->setBlock($this, $this, true, false); } public function onActivate(Item $item, Player $player = null) : bool{ - $this->getLevel()->setBlock($this, BlockFactory::get(Block::GLOWING_REDSTONE_ORE, $this->meta)); + $this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::GLOWING_REDSTONE_ORE, $this->meta)); return false; //this shouldn't prevent block placement } public function onNearbyBlockChange() : void{ - $this->getLevel()->setBlock($this, BlockFactory::get(Block::GLOWING_REDSTONE_ORE, $this->meta)); + $this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::GLOWING_REDSTONE_ORE, $this->meta)); } public function getToolType() : int{ diff --git a/src/pocketmine/block/Sapling.php b/src/pocketmine/block/Sapling.php index 682f2251a..2fdeb3ebf 100644 --- a/src/pocketmine/block/Sapling.php +++ b/src/pocketmine/block/Sapling.php @@ -59,7 +59,7 @@ class Sapling extends Flowable{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{ $down = $this->getSide(Vector3::SIDE_DOWN); if($down->getId() === self::GRASS or $down->getId() === self::DIRT or $down->getId() === self::FARMLAND){ - $this->getLevel()->setBlock($blockReplace, $this, true, true); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); return true; } @@ -70,7 +70,7 @@ class Sapling extends Flowable{ public function onActivate(Item $item, Player $player = null) : bool{ if($item->getId() === Item::DYE and $item->getDamage() === 0x0F){ //Bonemeal //TODO: change log type - Tree::growTree($this->getLevel(), $this->x, $this->y, $this->z, new Random(mt_rand()), $this->getVariant()); + Tree::growTree($this->getLevelNonNull(), $this->x, $this->y, $this->z, new Random(mt_rand()), $this->getVariant()); $item->pop(); @@ -82,7 +82,7 @@ class Sapling extends Flowable{ public function onNearbyBlockChange() : void{ if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){ - $this->getLevel()->useBreakOn($this); + $this->getLevelNonNull()->useBreakOn($this); } } @@ -93,10 +93,10 @@ class Sapling extends Flowable{ public function onRandomTick() : void{ if($this->level->getFullLightAt($this->x, $this->y, $this->z) >= 8 and mt_rand(1, 7) === 1){ if(($this->meta & 0x08) === 0x08){ - Tree::growTree($this->getLevel(), $this->x, $this->y, $this->z, new Random(mt_rand()), $this->getVariant()); + Tree::growTree($this->getLevelNonNull(), $this->x, $this->y, $this->z, new Random(mt_rand()), $this->getVariant()); }else{ $this->meta |= 0x08; - $this->getLevel()->setBlock($this, $this, true); + $this->getLevelNonNull()->setBlock($this, $this, true); } } } diff --git a/src/pocketmine/block/SignPost.php b/src/pocketmine/block/SignPost.php index bba195a22..c63307446 100644 --- a/src/pocketmine/block/SignPost.php +++ b/src/pocketmine/block/SignPost.php @@ -62,13 +62,13 @@ class SignPost extends Transparent{ if($face === Vector3::SIDE_UP){ $this->meta = $player !== null ? (floor((($player->yaw + 180) * 16 / 360) + 0.5) & 0x0f) : 0; - $this->getLevel()->setBlock($blockReplace, $this, true); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true); }else{ $this->meta = $face; - $this->getLevel()->setBlock($blockReplace, BlockFactory::get(Block::WALL_SIGN, $this->meta), true); + $this->getLevelNonNull()->setBlock($blockReplace, BlockFactory::get(Block::WALL_SIGN, $this->meta), true); } - Tile::createTile(Tile::SIGN, $this->getLevel(), TileSign::createNBT($this, $face, $item, $player)); + Tile::createTile(Tile::SIGN, $this->getLevelNonNull(), TileSign::createNBT($this, $face, $item, $player)); return true; } @@ -78,7 +78,7 @@ class SignPost extends Transparent{ public function onNearbyBlockChange() : void{ if($this->getSide(Vector3::SIDE_DOWN)->getId() === self::AIR){ - $this->getLevel()->useBreakOn($this); + $this->getLevelNonNull()->useBreakOn($this); } } diff --git a/src/pocketmine/block/Skull.php b/src/pocketmine/block/Skull.php index 3dfd1b04b..dfeb2f711 100644 --- a/src/pocketmine/block/Skull.php +++ b/src/pocketmine/block/Skull.php @@ -65,8 +65,8 @@ class Skull extends Flowable{ } $this->meta = $face; - $this->getLevel()->setBlock($blockReplace, $this, true); - Tile::createTile(Tile::SKULL, $this->getLevel(), TileSkull::createNBT($this, $face, $item, $player)); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true); + Tile::createTile(Tile::SKULL, $this->getLevelNonNull(), TileSkull::createNBT($this, $face, $item, $player)); return true; } diff --git a/src/pocketmine/block/Slab.php b/src/pocketmine/block/Slab.php index dcfa75923..3657cb1d3 100644 --- a/src/pocketmine/block/Slab.php +++ b/src/pocketmine/block/Slab.php @@ -56,11 +56,11 @@ abstract class Slab extends Transparent{ $this->meta &= 0x07; if($face === Vector3::SIDE_DOWN){ if($blockClicked->getId() === $this->id and ($blockClicked->getDamage() & 0x08) === 0x08 and $blockClicked->getVariant() === $this->getVariant()){ - $this->getLevel()->setBlock($blockClicked, BlockFactory::get($this->getDoubleSlabId(), $this->getVariant()), true); + $this->getLevelNonNull()->setBlock($blockClicked, BlockFactory::get($this->getDoubleSlabId(), $this->getVariant()), true); return true; }elseif($blockReplace->getId() === $this->id and $blockReplace->getVariant() === $this->getVariant()){ - $this->getLevel()->setBlock($blockReplace, BlockFactory::get($this->getDoubleSlabId(), $this->getVariant()), true); + $this->getLevelNonNull()->setBlock($blockReplace, BlockFactory::get($this->getDoubleSlabId(), $this->getVariant()), true); return true; }else{ @@ -68,18 +68,18 @@ abstract class Slab extends Transparent{ } }elseif($face === Vector3::SIDE_UP){ if($blockClicked->getId() === $this->id and ($blockClicked->getDamage() & 0x08) === 0 and $blockClicked->getVariant() === $this->getVariant()){ - $this->getLevel()->setBlock($blockClicked, BlockFactory::get($this->getDoubleSlabId(), $this->getVariant()), true); + $this->getLevelNonNull()->setBlock($blockClicked, BlockFactory::get($this->getDoubleSlabId(), $this->getVariant()), true); return true; }elseif($blockReplace->getId() === $this->id and $blockReplace->getVariant() === $this->getVariant()){ - $this->getLevel()->setBlock($blockReplace, BlockFactory::get($this->getDoubleSlabId(), $this->getVariant()), true); + $this->getLevelNonNull()->setBlock($blockReplace, BlockFactory::get($this->getDoubleSlabId(), $this->getVariant()), true); return true; } }else{ //TODO: collision if($blockReplace->getId() === $this->id){ if($blockReplace->getVariant() === $this->getVariant()){ - $this->getLevel()->setBlock($blockReplace, BlockFactory::get($this->getDoubleSlabId(), $this->getVariant()), true); + $this->getLevelNonNull()->setBlock($blockReplace, BlockFactory::get($this->getDoubleSlabId(), $this->getVariant()), true); return true; } @@ -95,7 +95,7 @@ abstract class Slab extends Transparent{ if($blockReplace->getId() === $this->id and $blockClicked->getVariant() !== $this->getVariant()){ return false; } - $this->getLevel()->setBlock($blockReplace, $this, true, true); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); return true; } diff --git a/src/pocketmine/block/SnowLayer.php b/src/pocketmine/block/SnowLayer.php index adb0aa7fb..8d11cab2a 100644 --- a/src/pocketmine/block/SnowLayer.php +++ b/src/pocketmine/block/SnowLayer.php @@ -66,7 +66,7 @@ class SnowLayer extends Flowable{ $this->setDamage($blockReplace->getDamage() + 1); } if($this->canBeSupportedBy($blockReplace->getSide(Vector3::SIDE_DOWN))){ - $this->getLevel()->setBlock($blockReplace, $this, true); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true); return true; } @@ -76,7 +76,7 @@ class SnowLayer extends Flowable{ public function onNearbyBlockChange() : void{ if(!$this->canBeSupportedBy($this->getSide(Vector3::SIDE_DOWN))){ - $this->getLevel()->setBlock($this, BlockFactory::get(Block::AIR), false, false); + $this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::AIR), false, false); } } @@ -86,7 +86,7 @@ class SnowLayer extends Flowable{ public function onRandomTick() : void{ if($this->level->getBlockLightAt($this->x, $this->y, $this->z) >= 12){ - $this->getLevel()->setBlock($this, BlockFactory::get(Block::AIR), false, false); + $this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::AIR), false, false); } } diff --git a/src/pocketmine/block/Stair.php b/src/pocketmine/block/Stair.php index cf9328aac..352bacd82 100644 --- a/src/pocketmine/block/Stair.php +++ b/src/pocketmine/block/Stair.php @@ -93,7 +93,7 @@ abstract class Stair extends Transparent{ if(($clickVector->y > 0.5 and $face !== Vector3::SIDE_UP) or $face === Vector3::SIDE_DOWN){ $this->meta |= 0x04; //Upside-down stairs } - $this->getLevel()->setBlock($blockReplace, $this, true, true); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); return true; } diff --git a/src/pocketmine/block/StandingBanner.php b/src/pocketmine/block/StandingBanner.php index bc8c9012c..090f5c83a 100644 --- a/src/pocketmine/block/StandingBanner.php +++ b/src/pocketmine/block/StandingBanner.php @@ -62,13 +62,13 @@ class StandingBanner extends Transparent{ if($face !== Vector3::SIDE_DOWN){ if($face === Vector3::SIDE_UP and $player !== null){ $this->meta = floor((($player->yaw + 180) * 16 / 360) + 0.5) & 0x0f; - $this->getLevel()->setBlock($blockReplace, $this, true); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true); }else{ $this->meta = $face; - $this->getLevel()->setBlock($blockReplace, BlockFactory::get(Block::WALL_BANNER, $this->meta), true); + $this->getLevelNonNull()->setBlock($blockReplace, BlockFactory::get(Block::WALL_BANNER, $this->meta), true); } - Tile::createTile(Tile::BANNER, $this->getLevel(), TileBanner::createNBT($this, $face, $item, $player)); + Tile::createTile(Tile::BANNER, $this->getLevelNonNull(), TileBanner::createNBT($this, $face, $item, $player)); return true; } @@ -77,7 +77,7 @@ class StandingBanner extends Transparent{ public function onNearbyBlockChange() : void{ if($this->getSide(Vector3::SIDE_DOWN)->getId() === self::AIR){ - $this->getLevel()->useBreakOn($this); + $this->getLevelNonNull()->useBreakOn($this); } } diff --git a/src/pocketmine/block/Sugarcane.php b/src/pocketmine/block/Sugarcane.php index ad4e95aff..cdda01f8e 100644 --- a/src/pocketmine/block/Sugarcane.php +++ b/src/pocketmine/block/Sugarcane.php @@ -46,20 +46,20 @@ class Sugarcane extends Flowable{ if($item->getId() === Item::DYE and $item->getDamage() === 0x0F){ //Bonemeal if($this->getSide(Vector3::SIDE_DOWN)->getId() !== self::SUGARCANE_BLOCK){ for($y = 1; $y < 3; ++$y){ - $b = $this->getLevel()->getBlockAt($this->x, $this->y + $y, $this->z); + $b = $this->getLevelNonNull()->getBlockAt($this->x, $this->y + $y, $this->z); if($b->getId() === self::AIR){ $ev = new BlockGrowEvent($b, BlockFactory::get(Block::SUGARCANE_BLOCK)); $ev->call(); if($ev->isCancelled()){ break; } - $this->getLevel()->setBlock($b, $ev->getNewState(), true); + $this->getLevelNonNull()->setBlock($b, $ev->getNewState(), true); }else{ break; } } $this->meta = 0; - $this->getLevel()->setBlock($this, $this, true); + $this->getLevelNonNull()->setBlock($this, $this, true); } $item->pop(); @@ -73,7 +73,7 @@ class Sugarcane extends Flowable{ public function onNearbyBlockChange() : void{ $down = $this->getSide(Vector3::SIDE_DOWN); if($down->isTransparent() and $down->getId() !== self::SUGARCANE_BLOCK){ - $this->getLevel()->useBreakOn($this); + $this->getLevelNonNull()->useBreakOn($this); } } @@ -85,17 +85,17 @@ class Sugarcane extends Flowable{ if($this->getSide(Vector3::SIDE_DOWN)->getId() !== self::SUGARCANE_BLOCK){ if($this->meta === 0x0F){ for($y = 1; $y < 3; ++$y){ - $b = $this->getLevel()->getBlockAt($this->x, $this->y + $y, $this->z); + $b = $this->getLevelNonNull()->getBlockAt($this->x, $this->y + $y, $this->z); if($b->getId() === self::AIR){ - $this->getLevel()->setBlock($b, BlockFactory::get(Block::SUGARCANE_BLOCK), true); + $this->getLevelNonNull()->setBlock($b, BlockFactory::get(Block::SUGARCANE_BLOCK), true); break; } } $this->meta = 0; - $this->getLevel()->setBlock($this, $this, true); + $this->getLevelNonNull()->setBlock($this, $this, true); }else{ ++$this->meta; - $this->getLevel()->setBlock($this, $this, true); + $this->getLevelNonNull()->setBlock($this, $this, true); } } } @@ -103,7 +103,7 @@ class Sugarcane extends Flowable{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{ $down = $this->getSide(Vector3::SIDE_DOWN); if($down->getId() === self::SUGARCANE_BLOCK){ - $this->getLevel()->setBlock($blockReplace, BlockFactory::get(Block::SUGARCANE_BLOCK), true); + $this->getLevelNonNull()->setBlock($blockReplace, BlockFactory::get(Block::SUGARCANE_BLOCK), true); return true; }elseif($down->getId() === self::GRASS or $down->getId() === self::DIRT or $down->getId() === self::SAND){ @@ -112,7 +112,7 @@ class Sugarcane extends Flowable{ $block2 = $down->getSide(Vector3::SIDE_WEST); $block3 = $down->getSide(Vector3::SIDE_EAST); if(($block0 instanceof Water) or ($block1 instanceof Water) or ($block2 instanceof Water) or ($block3 instanceof Water)){ - $this->getLevel()->setBlock($blockReplace, BlockFactory::get(Block::SUGARCANE_BLOCK), true); + $this->getLevelNonNull()->setBlock($blockReplace, BlockFactory::get(Block::SUGARCANE_BLOCK), true); return true; } diff --git a/src/pocketmine/block/TNT.php b/src/pocketmine/block/TNT.php index 44cfb6fbc..6c5f8252f 100644 --- a/src/pocketmine/block/TNT.php +++ b/src/pocketmine/block/TNT.php @@ -78,13 +78,13 @@ class TNT extends Solid{ * @return void */ public function ignite(int $fuse = 80){ - $this->getLevel()->setBlock($this, BlockFactory::get(Block::AIR), true); + $this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::AIR), true); $mot = (new Random())->nextSignedFloat() * M_PI * 2; $nbt = Entity::createBaseNBT($this->add(0.5, 0, 0.5), new Vector3(-sin($mot) * 0.02, 0.2, -cos($mot) * 0.02)); $nbt->setShort("Fuse", $fuse); - $tnt = Entity::createEntity("PrimedTNT", $this->getLevel(), $nbt); + $tnt = Entity::createEntity("PrimedTNT", $this->getLevelNonNull(), $nbt); if($tnt !== null){ $tnt->spawnToAll(); diff --git a/src/pocketmine/block/TallGrass.php b/src/pocketmine/block/TallGrass.php index 7bcd619fa..a072cfcba 100644 --- a/src/pocketmine/block/TallGrass.php +++ b/src/pocketmine/block/TallGrass.php @@ -53,7 +53,7 @@ class TallGrass extends Flowable{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{ $down = $this->getSide(Vector3::SIDE_DOWN)->getId(); if($down === self::GRASS or $down === self::DIRT){ - $this->getLevel()->setBlock($blockReplace, $this, true); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true); return true; } @@ -63,7 +63,7 @@ class TallGrass extends Flowable{ public function onNearbyBlockChange() : void{ if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){ //Replace with common break method - $this->getLevel()->setBlock($this, BlockFactory::get(Block::AIR), true, true); + $this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::AIR), true, true); } } diff --git a/src/pocketmine/block/Torch.php b/src/pocketmine/block/Torch.php index e88767cfd..d95f3b119 100644 --- a/src/pocketmine/block/Torch.php +++ b/src/pocketmine/block/Torch.php @@ -57,7 +57,7 @@ class Torch extends Flowable{ $face = $faces[$meta] ?? Vector3::SIDE_DOWN; if($this->getSide($face)->isTransparent() and !($face === Vector3::SIDE_DOWN and ($below->getId() === self::FENCE or $below->getId() === self::COBBLESTONE_WALL))){ - $this->getLevel()->useBreakOn($this); + $this->getLevelNonNull()->useBreakOn($this); } } @@ -73,12 +73,12 @@ class Torch extends Flowable{ Vector3::SIDE_EAST => 1 ]; $this->meta = $faces[$face]; - $this->getLevel()->setBlock($blockReplace, $this, true, true); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); return true; }elseif(!$below->isTransparent() or $below->getId() === self::FENCE or $below->getId() === self::COBBLESTONE_WALL){ $this->meta = 0; - $this->getLevel()->setBlock($blockReplace, $this, true, true); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); return true; } diff --git a/src/pocketmine/block/Trapdoor.php b/src/pocketmine/block/Trapdoor.php index 25c747cbd..0cbbd1564 100644 --- a/src/pocketmine/block/Trapdoor.php +++ b/src/pocketmine/block/Trapdoor.php @@ -136,7 +136,7 @@ class Trapdoor extends Transparent{ if(($clickVector->y > 0.5 and $face !== self::SIDE_UP) or $face === self::SIDE_DOWN){ $this->meta |= self::MASK_UPPER; //top half of block } - $this->getLevel()->setBlock($blockReplace, $this, true, true); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); return true; } @@ -146,7 +146,7 @@ class Trapdoor extends Transparent{ public function onActivate(Item $item, Player $player = null) : bool{ $this->meta ^= self::MASK_OPENED; - $this->getLevel()->setBlock($this, $this, true); + $this->getLevelNonNull()->setBlock($this, $this, true); $this->level->addSound(new DoorSound($this)); return true; } diff --git a/src/pocketmine/block/Vine.php b/src/pocketmine/block/Vine.php index dadc7a329..5473fd247 100644 --- a/src/pocketmine/block/Vine.php +++ b/src/pocketmine/block/Vine.php @@ -150,7 +150,7 @@ class Vine extends Flowable{ $this->meta |= $blockReplace->meta; } - $this->getLevel()->setBlock($blockReplace, $this, true, true); + $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); return true; } diff --git a/src/pocketmine/block/WallBanner.php b/src/pocketmine/block/WallBanner.php index a0a16ea44..90a9b0271 100644 --- a/src/pocketmine/block/WallBanner.php +++ b/src/pocketmine/block/WallBanner.php @@ -33,7 +33,7 @@ class WallBanner extends StandingBanner{ public function onNearbyBlockChange() : void{ if($this->getSide($this->meta ^ 0x01)->getId() === self::AIR){ - $this->getLevel()->useBreakOn($this); + $this->getLevelNonNull()->useBreakOn($this); } } } diff --git a/src/pocketmine/block/WallSign.php b/src/pocketmine/block/WallSign.php index 58b0d8a82..9714ad5eb 100644 --- a/src/pocketmine/block/WallSign.php +++ b/src/pocketmine/block/WallSign.php @@ -33,7 +33,7 @@ class WallSign extends SignPost{ public function onNearbyBlockChange() : void{ if($this->getSide($this->meta ^ 0x01)->getId() === self::AIR){ - $this->getLevel()->useBreakOn($this); + $this->getLevelNonNull()->useBreakOn($this); } } } diff --git a/src/pocketmine/block/Water.php b/src/pocketmine/block/Water.php index 0e6267b06..8a17a2ae8 100644 --- a/src/pocketmine/block/Water.php +++ b/src/pocketmine/block/Water.php @@ -73,8 +73,8 @@ class Water extends Liquid{ } public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{ - $ret = $this->getLevel()->setBlock($this, $this, true, false); - $this->getLevel()->scheduleDelayedBlockUpdate($this, $this->tickRate()); + $ret = $this->getLevelNonNull()->setBlock($this, $this, true, false); + $this->getLevelNonNull()->scheduleDelayedBlockUpdate($this, $this->tickRate()); return $ret; } diff --git a/src/pocketmine/block/WaterLily.php b/src/pocketmine/block/WaterLily.php index 8d4ebc7cf..ea9010fc2 100644 --- a/src/pocketmine/block/WaterLily.php +++ b/src/pocketmine/block/WaterLily.php @@ -59,7 +59,7 @@ class WaterLily extends Flowable{ if($blockClicked instanceof Water){ $up = $blockClicked->getSide(Vector3::SIDE_UP); if($up->getId() === Block::AIR){ - $this->getLevel()->setBlock($up, $this, true, true); + $this->getLevelNonNull()->setBlock($up, $this, true, true); return true; } } @@ -69,7 +69,7 @@ class WaterLily extends Flowable{ public function onNearbyBlockChange() : void{ if(!($this->getSide(Vector3::SIDE_DOWN) instanceof Water)){ - $this->getLevel()->useBreakOn($this); + $this->getLevelNonNull()->useBreakOn($this); } } diff --git a/src/pocketmine/block/Wood.php b/src/pocketmine/block/Wood.php index 2c309aed6..707256c6e 100644 --- a/src/pocketmine/block/Wood.php +++ b/src/pocketmine/block/Wood.php @@ -56,7 +56,7 @@ class Wood extends Solid{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{ $this->meta = PillarRotationHelper::getMetaFromFace($this->meta, $face); - return $this->getLevel()->setBlock($blockReplace, $this, true, true); + return $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); } public function getVariantBitmask() : int{ diff --git a/src/pocketmine/command/CommandReader.php b/src/pocketmine/command/CommandReader.php index 656d82464..5a779b48c 100644 --- a/src/pocketmine/command/CommandReader.php +++ b/src/pocketmine/command/CommandReader.php @@ -68,7 +68,7 @@ class CommandReader extends Thread{ $opts = getopt("", ["disable-readline", "enable-readline"]); - if(extension_loaded("readline") and (Utils::getOS() === "win" ? isset($opts["enable-readline"]) : !isset($opts["disable-readline"])) and !$this->isPipe(STDIN)){ + if(extension_loaded("readline") and (Utils::getOS() === Utils::OS_WINDOWS ? isset($opts["enable-readline"]) : !isset($opts["disable-readline"])) and !$this->isPipe(STDIN)){ $this->type = self::TYPE_READLINE; } } diff --git a/src/pocketmine/command/defaults/GiveCommand.php b/src/pocketmine/command/defaults/GiveCommand.php index 8e2138aa0..fc9a96b93 100644 --- a/src/pocketmine/command/defaults/GiveCommand.php +++ b/src/pocketmine/command/defaults/GiveCommand.php @@ -62,7 +62,7 @@ class GiveCommand extends VanillaCommand{ } try{ - $item = ItemFactory::fromString($args[1]); + $item = ItemFactory::fromStringSingle($args[1]); }catch(\InvalidArgumentException $e){ $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.give.item.notFound", [$args[1]])); return true; diff --git a/src/pocketmine/command/defaults/ParticleCommand.php b/src/pocketmine/command/defaults/ParticleCommand.php index d2ece9b06..3c7424952 100644 --- a/src/pocketmine/command/defaults/ParticleCommand.php +++ b/src/pocketmine/command/defaults/ParticleCommand.php @@ -92,7 +92,7 @@ class ParticleCommand extends VanillaCommand{ } if($sender instanceof Player){ - $level = $sender->getLevel(); + $level = $sender->getLevelNonNull(); $pos = new Vector3( $this->getRelativeDouble($sender->getX(), $sender, $args[1]), $this->getRelativeDouble($sender->getY(), $sender, $args[2], 0, Level::Y_MAX), diff --git a/src/pocketmine/command/defaults/SeedCommand.php b/src/pocketmine/command/defaults/SeedCommand.php index 431c04d48..74cf328fa 100644 --- a/src/pocketmine/command/defaults/SeedCommand.php +++ b/src/pocketmine/command/defaults/SeedCommand.php @@ -44,7 +44,7 @@ class SeedCommand extends VanillaCommand{ } if($sender instanceof Player){ - $seed = $sender->getLevel()->getSeed(); + $seed = $sender->getLevelNonNull()->getSeed(); }else{ $seed = $sender->getServer()->getDefaultLevel()->getSeed(); } diff --git a/src/pocketmine/command/defaults/SetWorldSpawnCommand.php b/src/pocketmine/command/defaults/SetWorldSpawnCommand.php index 054adc059..6493ec296 100644 --- a/src/pocketmine/command/defaults/SetWorldSpawnCommand.php +++ b/src/pocketmine/command/defaults/SetWorldSpawnCommand.php @@ -51,7 +51,7 @@ class SetWorldSpawnCommand extends VanillaCommand{ if(count($args) === 0){ if($sender instanceof Player){ - $level = $sender->getLevel(); + $level = $sender->getLevelNonNull(); $pos = (new Vector3($sender->x, $sender->y, $sender->z))->round(); }else{ $sender->sendMessage(TextFormat::RED . "You can only perform this command as a player"); diff --git a/src/pocketmine/command/defaults/SpawnpointCommand.php b/src/pocketmine/command/defaults/SpawnpointCommand.php index 775e896f5..13c7e6d33 100644 --- a/src/pocketmine/command/defaults/SpawnpointCommand.php +++ b/src/pocketmine/command/defaults/SpawnpointCommand.php @@ -71,7 +71,7 @@ class SpawnpointCommand extends VanillaCommand{ if(count($args) === 4){ if($target->isValid()){ - $level = $target->getLevel(); + $level = $target->getLevelNonNull(); $pos = $sender instanceof Player ? $sender->getPosition() : $level->getSpawnLocation(); $x = $this->getRelativeDouble($pos->x, $sender, $args[1]); $y = $this->getRelativeDouble($pos->y, $sender, $args[2], 0, Level::Y_MAX); @@ -84,7 +84,7 @@ class SpawnpointCommand extends VanillaCommand{ } }elseif(count($args) <= 1){ if($sender instanceof Player){ - $pos = new Position($sender->getFloorX(), $sender->getFloorY(), $sender->getFloorZ(), $sender->getLevel()); + $pos = new Position($sender->getFloorX(), $sender->getFloorY(), $sender->getFloorZ(), $sender->getLevelNonNull()); $target->setSpawn($pos); Command::broadcastCommandMessage($sender, new TranslationContainer("commands.spawnpoint.success", [$target->getName(), round($pos->x, 2), round($pos->y, 2), round($pos->z, 2)])); diff --git a/src/pocketmine/command/defaults/StatusCommand.php b/src/pocketmine/command/defaults/StatusCommand.php index d43f7df28..60d49c2dc 100644 --- a/src/pocketmine/command/defaults/StatusCommand.php +++ b/src/pocketmine/command/defaults/StatusCommand.php @@ -24,8 +24,8 @@ declare(strict_types=1); namespace pocketmine\command\defaults; use pocketmine\command\CommandSender; +use pocketmine\utils\Process; use pocketmine\utils\TextFormat; -use pocketmine\utils\Utils; use function count; use function floor; use function microtime; @@ -48,8 +48,8 @@ class StatusCommand extends VanillaCommand{ return true; } - $rUsage = Utils::getRealMemoryUsage(); - $mUsage = Utils::getMemoryUsage(true); + $rUsage = Process::getRealMemoryUsage(); + $mUsage = Process::getAdvancedMemoryUsage(); $server = $sender->getServer(); $sender->sendMessage(TextFormat::GREEN . "---- " . TextFormat::WHITE . "Server status" . TextFormat::GREEN . " ----"); @@ -94,7 +94,7 @@ class StatusCommand extends VanillaCommand{ $sender->sendMessage(TextFormat::GOLD . "Network upload: " . TextFormat::RED . round($server->getNetwork()->getUpload() / 1024, 2) . " kB/s"); $sender->sendMessage(TextFormat::GOLD . "Network download: " . TextFormat::RED . round($server->getNetwork()->getDownload() / 1024, 2) . " kB/s"); - $sender->sendMessage(TextFormat::GOLD . "Thread count: " . TextFormat::RED . Utils::getThreadCount()); + $sender->sendMessage(TextFormat::GOLD . "Thread count: " . TextFormat::RED . Process::getThreadCount()); $sender->sendMessage(TextFormat::GOLD . "Main thread memory: " . TextFormat::RED . number_format(round(($mUsage[0] / 1024) / 1024, 2), 2) . " MB."); $sender->sendMessage(TextFormat::GOLD . "Total memory: " . TextFormat::RED . number_format(round(($mUsage[1] / 1024) / 1024, 2), 2) . " MB."); diff --git a/src/pocketmine/command/defaults/TimeCommand.php b/src/pocketmine/command/defaults/TimeCommand.php index bd94e2ae5..41c2903bc 100644 --- a/src/pocketmine/command/defaults/TimeCommand.php +++ b/src/pocketmine/command/defaults/TimeCommand.php @@ -77,7 +77,7 @@ class TimeCommand extends VanillaCommand{ return true; } if($sender instanceof Player){ - $level = $sender->getLevel(); + $level = $sender->getLevelNonNull(); }else{ $level = $sender->getServer()->getDefaultLevel(); } @@ -96,12 +96,28 @@ class TimeCommand extends VanillaCommand{ return true; } - if($args[1] === "day"){ - $value = Level::TIME_DAY; - }elseif($args[1] === "night"){ - $value = Level::TIME_NIGHT; - }else{ - $value = $this->getInteger($sender, $args[1], 0); + switch($args[1]){ + case "day": + $value = Level::TIME_DAY; + break; + case "noon": + $value = Level::TIME_NOON; + break; + case "sunset": + $value = Level::TIME_SUNSET; + break; + case "night": + $value = Level::TIME_NIGHT; + break; + case "midnight": + $value = Level::TIME_MIDNIGHT; + break; + case "sunrise": + $value = Level::TIME_SUNRISE; + break; + default: + $value = $this->getInteger($sender, $args[1], 0); + break; } foreach($sender->getServer()->getLevels() as $level){ diff --git a/src/pocketmine/command/defaults/TitleCommand.php b/src/pocketmine/command/defaults/TitleCommand.php index 667eeee08..030888912 100644 --- a/src/pocketmine/command/defaults/TitleCommand.php +++ b/src/pocketmine/command/defaults/TitleCommand.php @@ -68,21 +68,21 @@ class TitleCommand extends VanillaCommand{ throw new InvalidCommandSyntaxException(); } - $player->addTitle(implode(" ", array_slice($args, 2))); + $player->sendTitle(implode(" ", array_slice($args, 2))); break; case "subtitle": if(count($args) < 3){ throw new InvalidCommandSyntaxException(); } - $player->addSubTitle(implode(" ", array_slice($args, 2))); + $player->sendSubTitle(implode(" ", array_slice($args, 2))); break; case "actionbar": if(count($args) < 3){ throw new InvalidCommandSyntaxException(); } - $player->addActionBarMessage(implode(" ", array_slice($args, 2))); + $player->sendActionBarMessage(implode(" ", array_slice($args, 2))); break; case "times": if(count($args) < 5){ diff --git a/src/pocketmine/entity/Entity.php b/src/pocketmine/entity/Entity.php index 7d207eacc..97ac40b4b 100644 --- a/src/pocketmine/entity/Entity.php +++ b/src/pocketmine/entity/Entity.php @@ -1750,7 +1750,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{ } if($pos instanceof Position and $pos->level !== null and $pos->level !== $this->level){ - if(!$this->switchLevel($pos->getLevel())){ + if(!$this->switchLevel($pos->getLevelNonNull())){ return false; } } @@ -1856,7 +1856,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{ $pitch = $pitch ?? $pos->pitch; } $from = Position::fromObject($this, $this->level); - $to = Position::fromObject($pos, $pos instanceof Position ? $pos->getLevel() : $this->level); + $to = Position::fromObject($pos, $pos instanceof Position ? $pos->getLevelNonNull() : $this->level); $ev = new EntityTeleportEvent($this, $from, $to); $ev->call(); if($ev->isCancelled()){ @@ -1921,7 +1921,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{ protected function sendSpawnPacket(Player $player) : void{ $pk = new AddActorPacket(); $pk->entityRuntimeId = $this->getId(); - $pk->type = static::NETWORK_ID; + $pk->type = AddActorPacket::LEGACY_ID_MAP_BC[static::NETWORK_ID]; $pk->position = $this->asVector3(); $pk->motion = $this->getMotion(); $pk->yaw = $this->yaw; diff --git a/src/pocketmine/entity/Living.php b/src/pocketmine/entity/Living.php index 9ffb0b8dd..b38c4e023 100644 --- a/src/pocketmine/entity/Living.php +++ b/src/pocketmine/entity/Living.php @@ -191,7 +191,7 @@ abstract class Living extends Entity implements Damageable{ public function hasLineOfSight(Entity $entity) : bool{ //TODO: head height return true; - //return $this->getLevel()->rayTraceBlocks(Vector3::createVector($this->x, $this->y + $this->height, $this->z), Vector3::createVector($entity->x, $entity->y + $entity->height, $entity->z)) === null; + //return $this->getLevelNonNull()->rayTraceBlocks(Vector3::createVector($this->x, $this->y + $this->height, $this->z), Vector3::createVector($entity->x, $entity->y + $entity->height, $entity->z)) === null; } /** @@ -608,15 +608,14 @@ abstract class Living extends Entity implements Damageable{ } protected function onDeath() : void{ - $ev = new EntityDeathEvent($this, $this->getDrops()); + $ev = new EntityDeathEvent($this, $this->getDrops(), $this->getXpDropAmount()); $ev->call(); foreach($ev->getDrops() as $item){ - $this->getLevel()->dropItem($this, $item); + $this->getLevelNonNull()->dropItem($this, $item); } //TODO: check death conditions (must have been damaged by player < 5 seconds from death) - //TODO: allow this number to be manipulated during EntityDeathEvent - $this->level->dropExperience($this, $this->getXpDropAmount()); + $this->level->dropExperience($this, $ev->getXpDropAmount()); } protected function onDeathUpdate(int $tickDiff) : bool{ diff --git a/src/pocketmine/entity/object/FallingBlock.php b/src/pocketmine/entity/object/FallingBlock.php index 3e516e4fc..37affc309 100644 --- a/src/pocketmine/entity/object/FallingBlock.php +++ b/src/pocketmine/entity/object/FallingBlock.php @@ -98,7 +98,7 @@ class FallingBlock extends Entity{ $hasUpdate = parent::entityBaseTick($tickDiff); if(!$this->isFlaggedForDespawn()){ - $pos = Position::fromObject($this->add(-$this->width / 2, $this->height, -$this->width / 2)->floor(), $this->getLevel()); + $pos = Position::fromObject($this->add(-$this->width / 2, $this->height, -$this->width / 2)->floor(), $this->getLevelNonNull()); $this->block->position($pos); @@ -113,12 +113,12 @@ class FallingBlock extends Entity{ $block = $this->level->getBlock($pos); if(!$block->canBeReplaced() or ($this->onGround and abs($this->y - $this->getFloorY()) > 0.001)){ //FIXME: anvils are supposed to destroy torches - $this->getLevel()->dropItem($this, ItemFactory::get($this->getBlock(), $this->getDamage())); + $this->getLevelNonNull()->dropItem($this, ItemFactory::get($this->getBlock(), $this->getDamage())); }else{ $ev = new EntityBlockChangeEvent($this, $block, $blockTarget ?? $this->block); $ev->call(); if(!$ev->isCancelled()){ - $this->getLevel()->setBlock($pos, $ev->getTo(), true); + $this->getLevelNonNull()->setBlock($pos, $ev->getTo(), true); } } $hasUpdate = true; diff --git a/src/pocketmine/event/entity/EntityDamageByChildEntityEvent.php b/src/pocketmine/event/entity/EntityDamageByChildEntityEvent.php index 764d844da..1ad1e5b0e 100644 --- a/src/pocketmine/event/entity/EntityDamageByChildEntityEvent.php +++ b/src/pocketmine/event/entity/EntityDamageByChildEntityEvent.php @@ -44,6 +44,6 @@ class EntityDamageByChildEntityEvent extends EntityDamageByEntityEvent{ * Returns the entity which caused the damage, or null if the entity has been killed or closed. */ public function getChild() : ?Entity{ - return $this->getEntity()->getLevel()->getServer()->findEntity($this->childEntityEid); + return $this->getEntity()->getLevelNonNull()->getServer()->findEntity($this->childEntityEid); } } diff --git a/src/pocketmine/event/entity/EntityDamageByEntityEvent.php b/src/pocketmine/event/entity/EntityDamageByEntityEvent.php index 3bef428ad..28241e6d2 100644 --- a/src/pocketmine/event/entity/EntityDamageByEntityEvent.php +++ b/src/pocketmine/event/entity/EntityDamageByEntityEvent.php @@ -62,7 +62,7 @@ class EntityDamageByEntityEvent extends EntityDamageEvent{ * Returns the attacking entity, or null if the attacker has been killed or closed. */ public function getDamager() : ?Entity{ - return $this->getEntity()->getLevel()->getServer()->findEntity($this->damagerEntityId); + return $this->getEntity()->getLevelNonNull()->getServer()->findEntity($this->damagerEntityId); } public function getKnockBack() : float{ diff --git a/src/pocketmine/event/entity/EntityDeathEvent.php b/src/pocketmine/event/entity/EntityDeathEvent.php index 51e8186b2..4eb9953ab 100644 --- a/src/pocketmine/event/entity/EntityDeathEvent.php +++ b/src/pocketmine/event/entity/EntityDeathEvent.php @@ -29,13 +29,16 @@ use pocketmine\item\Item; class EntityDeathEvent extends EntityEvent{ /** @var Item[] */ private $drops = []; + /** @var int */ + private $xp; /** * @param Item[] $drops */ - public function __construct(Living $entity, array $drops = []){ + public function __construct(Living $entity, array $drops = [], int $xp = 0){ $this->entity = $entity; $this->drops = $drops; + $this->xp = $xp; } /** @@ -58,4 +61,21 @@ class EntityDeathEvent extends EntityEvent{ public function setDrops(array $drops) : void{ $this->drops = $drops; } + + /** + * Returns how much experience is dropped due to this entity's death. + */ + public function getXpDropAmount() : int{ + return $this->xp; + } + + /** + * @throws \InvalidArgumentException + */ + public function setXpDropAmount(int $xp) : void{ + if($xp < 0){ + throw new \InvalidArgumentException("XP drop amount must not be negative"); + } + $this->xp = $xp; + } } diff --git a/src/pocketmine/event/entity/EntityDespawnEvent.php b/src/pocketmine/event/entity/EntityDespawnEvent.php index cf1500c84..ca6850f6c 100644 --- a/src/pocketmine/event/entity/EntityDespawnEvent.php +++ b/src/pocketmine/event/entity/EntityDespawnEvent.php @@ -42,26 +42,44 @@ class EntityDespawnEvent extends EntityEvent{ $this->entityType = $entity::NETWORK_ID; } + /** + * @deprecated + */ public function getType() : int{ return $this->entityType; } + /** + * @deprecated + */ public function isCreature() : bool{ return $this->entity instanceof Creature; } + /** + * @deprecated + */ public function isHuman() : bool{ return $this->entity instanceof Human; } + /** + * @deprecated + */ public function isProjectile() : bool{ return $this->entity instanceof Projectile; } + /** + * @deprecated + */ public function isVehicle() : bool{ return $this->entity instanceof Vehicle; } + /** + * @deprecated + */ public function isItem() : bool{ return $this->entity instanceof ItemEntity; } diff --git a/src/pocketmine/event/entity/EntitySpawnEvent.php b/src/pocketmine/event/entity/EntitySpawnEvent.php index d1b437d7c..beb00bd32 100644 --- a/src/pocketmine/event/entity/EntitySpawnEvent.php +++ b/src/pocketmine/event/entity/EntitySpawnEvent.php @@ -43,30 +43,51 @@ class EntitySpawnEvent extends EntityEvent{ $this->entityType = $entity::NETWORK_ID; } + /** + * @deprecated + */ public function getPosition() : Position{ return $this->entity->getPosition(); } + /** + * @deprecated + */ public function getType() : int{ return $this->entityType; } + /** + * @deprecated + */ public function isCreature() : bool{ return $this->entity instanceof Creature; } + /** + * @deprecated + */ public function isHuman() : bool{ return $this->entity instanceof Human; } + /** + * @deprecated + */ public function isProjectile() : bool{ return $this->entity instanceof Projectile; } + /** + * @deprecated + */ public function isVehicle() : bool{ return $this->entity instanceof Vehicle; } + /** + * @deprecated + */ public function isItem() : bool{ return $this->entity instanceof ItemEntity; } diff --git a/src/pocketmine/event/player/PlayerDeathEvent.php b/src/pocketmine/event/player/PlayerDeathEvent.php index 46ddc34a0..84d539a11 100644 --- a/src/pocketmine/event/player/PlayerDeathEvent.php +++ b/src/pocketmine/event/player/PlayerDeathEvent.php @@ -47,8 +47,8 @@ class PlayerDeathEvent extends EntityDeathEvent{ * @param Item[] $drops * @param string|TextContainer|null $deathMessage Null will cause the default vanilla message to be used */ - public function __construct(Player $entity, array $drops, $deathMessage = null){ - parent::__construct($entity, $drops); + public function __construct(Player $entity, array $drops, $deathMessage = null, int $xp = 0){ + parent::__construct($entity, $drops, $xp); $this->deathMessage = $deathMessage ?? self::deriveMessage($entity->getDisplayName(), $entity->getLastDamageCause()); } diff --git a/src/pocketmine/event/server/LowMemoryEvent.php b/src/pocketmine/event/server/LowMemoryEvent.php index 2b02dc054..7c7a090a6 100644 --- a/src/pocketmine/event/server/LowMemoryEvent.php +++ b/src/pocketmine/event/server/LowMemoryEvent.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace pocketmine\event\server; -use pocketmine\utils\Utils; +use pocketmine\utils\Process; /** * Called when the server is in a low-memory state as defined by the properties @@ -75,6 +75,7 @@ class LowMemoryEvent extends ServerEvent{ * Amount of memory already freed */ public function getMemoryFreed() : int{ - return $this->getMemory() - ($this->isGlobal() ? Utils::getMemoryUsage(true)[1] : Utils::getMemoryUsage(true)[0]); + $usage = Process::getAdvancedMemoryUsage(); + return $this->getMemory() - ($this->isGlobal() ? $usage[1] : $usage[0]); } } diff --git a/src/pocketmine/inventory/ChestInventory.php b/src/pocketmine/inventory/ChestInventory.php index b7cd3765e..0f45ef431 100644 --- a/src/pocketmine/inventory/ChestInventory.php +++ b/src/pocketmine/inventory/ChestInventory.php @@ -74,7 +74,7 @@ class ChestInventory extends ContainerInventory{ if(count($this->getViewers()) === 1 and $this->getHolder()->isValid()){ //TODO: this crap really shouldn't be managed by the inventory $this->broadcastBlockEventPacket(true); - $this->getHolder()->getLevel()->broadcastLevelSoundEvent($this->getHolder()->add(0.5, 0.5, 0.5), $this->getOpenSound()); + $this->getHolder()->getLevelNonNull()->broadcastLevelSoundEvent($this->getHolder()->add(0.5, 0.5, 0.5), $this->getOpenSound()); } } @@ -82,7 +82,7 @@ class ChestInventory extends ContainerInventory{ if(count($this->getViewers()) === 1 and $this->getHolder()->isValid()){ //TODO: this crap really shouldn't be managed by the inventory $this->broadcastBlockEventPacket(false); - $this->getHolder()->getLevel()->broadcastLevelSoundEvent($this->getHolder()->add(0.5, 0.5, 0.5), $this->getCloseSound()); + $this->getHolder()->getLevelNonNull()->broadcastLevelSoundEvent($this->getHolder()->add(0.5, 0.5, 0.5), $this->getCloseSound()); } parent::onClose($who); } @@ -96,6 +96,6 @@ class ChestInventory extends ContainerInventory{ $pk->z = (int) $holder->z; $pk->eventType = 1; //it's always 1 for a chest $pk->eventData = $isOpen ? 1 : 0; - $holder->getLevel()->broadcastPacketToViewers($holder, $pk); + $holder->getLevelNonNull()->broadcastPacketToViewers($holder, $pk); } } diff --git a/src/pocketmine/inventory/CraftingManager.php b/src/pocketmine/inventory/CraftingManager.php index 661811fb7..41e1f2a63 100644 --- a/src/pocketmine/inventory/CraftingManager.php +++ b/src/pocketmine/inventory/CraftingManager.php @@ -60,7 +60,7 @@ class CraftingManager{ if($recipe["block"] !== "crafting_table"){ //TODO: filter others out for now to avoid breaking economics break; } - $this->registerRecipe(new ShapelessRecipe( + $this->registerShapelessRecipe(new ShapelessRecipe( array_map($itemDeserializerFunc, $recipe["input"]), array_map($itemDeserializerFunc, $recipe["output"]) )); @@ -69,7 +69,7 @@ class CraftingManager{ if($recipe["block"] !== "crafting_table"){ //TODO: filter others out for now to avoid breaking economics break; } - $this->registerRecipe(new ShapedRecipe( + $this->registerShapedRecipe(new ShapedRecipe( $recipe["shape"], array_map($itemDeserializerFunc, $recipe["input"]), array_map($itemDeserializerFunc, $recipe["output"]) @@ -79,7 +79,7 @@ class CraftingManager{ if($recipe["block"] !== "furnace"){ //TODO: filter others out for now to avoid breaking economics break; } - $this->registerRecipe(new FurnaceRecipe( + $this->registerFurnaceRecipe(new FurnaceRecipe( Item::jsonDeserialize($recipe["output"]), Item::jsonDeserialize($recipe["input"])) ); @@ -281,6 +281,9 @@ class CraftingManager{ return $this->furnaceRecipes[$input->getId() . ":" . $input->getDamage()] ?? $this->furnaceRecipes[$input->getId() . ":?"] ?? null; } + /** + * @deprecated + */ public function registerRecipe(Recipe $recipe) : void{ $recipe->registerToCraftingManager($this); } diff --git a/src/pocketmine/inventory/EnderChestInventory.php b/src/pocketmine/inventory/EnderChestInventory.php index 5203a589e..6c8a7c6ce 100644 --- a/src/pocketmine/inventory/EnderChestInventory.php +++ b/src/pocketmine/inventory/EnderChestInventory.php @@ -56,7 +56,7 @@ class EnderChestInventory extends ChestInventory{ */ public function setHolderPosition(EnderChest $enderChest){ $this->holder->setComponents($enderChest->getFloorX(), $enderChest->getFloorY(), $enderChest->getFloorZ()); - $this->holder->setLevel($enderChest->getLevel()); + $this->holder->setLevel($enderChest->getLevelNonNull()); } protected function getOpenSound() : int{ diff --git a/src/pocketmine/inventory/FurnaceRecipe.php b/src/pocketmine/inventory/FurnaceRecipe.php index 4f735f049..185f05ee7 100644 --- a/src/pocketmine/inventory/FurnaceRecipe.php +++ b/src/pocketmine/inventory/FurnaceRecipe.php @@ -53,6 +53,9 @@ class FurnaceRecipe implements Recipe{ return clone $this->output; } + /** + * @deprecated + */ public function registerToCraftingManager(CraftingManager $manager) : void{ $manager->registerFurnaceRecipe($this); } diff --git a/src/pocketmine/inventory/PlayerInventory.php b/src/pocketmine/inventory/PlayerInventory.php index 4fa1ebed7..7d7d159c6 100644 --- a/src/pocketmine/inventory/PlayerInventory.php +++ b/src/pocketmine/inventory/PlayerInventory.php @@ -173,7 +173,7 @@ class PlayerInventory extends BaseInventory{ $this->sendSlot($this->getHeldItemIndex(), $target); } }else{ - $this->getHolder()->getLevel()->getServer()->broadcastPacket($target, $pk); + $this->getHolder()->getLevelNonNull()->getServer()->broadcastPacket($target, $pk); if(in_array($this->getHolder(), $target, true)){ $this->sendSlot($this->getHeldItemIndex(), $this->getHolder()); } diff --git a/src/pocketmine/inventory/Recipe.php b/src/pocketmine/inventory/Recipe.php index 883eea6d9..917c32400 100644 --- a/src/pocketmine/inventory/Recipe.php +++ b/src/pocketmine/inventory/Recipe.php @@ -23,7 +23,13 @@ declare(strict_types=1); namespace pocketmine\inventory; +/** + * @deprecated + */ interface Recipe{ + /** + * @deprecated + */ public function registerToCraftingManager(CraftingManager $manager) : void; } diff --git a/src/pocketmine/inventory/ShapedRecipe.php b/src/pocketmine/inventory/ShapedRecipe.php index f43ebabea..a8126c55c 100644 --- a/src/pocketmine/inventory/ShapedRecipe.php +++ b/src/pocketmine/inventory/ShapedRecipe.php @@ -176,6 +176,9 @@ class ShapedRecipe implements CraftingRecipe{ return $this->shape; } + /** + * @deprecated + */ public function registerToCraftingManager(CraftingManager $manager) : void{ $manager->registerShapedRecipe($this); } diff --git a/src/pocketmine/inventory/ShapelessRecipe.php b/src/pocketmine/inventory/ShapelessRecipe.php index 32c8eed02..f277bf226 100644 --- a/src/pocketmine/inventory/ShapelessRecipe.php +++ b/src/pocketmine/inventory/ShapelessRecipe.php @@ -105,6 +105,9 @@ class ShapelessRecipe implements CraftingRecipe{ return $count; } + /** + * @deprecated + */ public function registerToCraftingManager(CraftingManager $manager) : void{ $manager->registerShapelessRecipe($this); } diff --git a/src/pocketmine/item/Bow.php b/src/pocketmine/item/Bow.php index 85bcfc021..2d1d026e7 100644 --- a/src/pocketmine/item/Bow.php +++ b/src/pocketmine/item/Bow.php @@ -64,7 +64,7 @@ class Bow extends Tool{ $p = $diff / 20; $baseForce = min((($p ** 2) + $p * 2) / 3, 1); - $entity = Entity::createEntity("Arrow", $player->getLevel(), $nbt, $player, $baseForce >= 1); + $entity = Entity::createEntity("Arrow", $player->getLevelNonNull(), $nbt, $player, $baseForce >= 1); if($entity instanceof Projectile){ $infinity = $this->hasEnchantment(Enchantment::INFINITY); if($entity instanceof ArrowEntity){ @@ -110,7 +110,7 @@ class Bow extends Tool{ $ev->getProjectile()->flagForDespawn(); }else{ $ev->getProjectile()->spawnToAll(); - $player->getLevel()->broadcastLevelSoundEvent($player, LevelSoundEventPacket::SOUND_BOW); + $player->getLevelNonNull()->broadcastLevelSoundEvent($player, LevelSoundEventPacket::SOUND_BOW); } }else{ $entity->spawnToAll(); diff --git a/src/pocketmine/item/Bucket.php b/src/pocketmine/item/Bucket.php index 717a4b5c6..0cd99f5a8 100644 --- a/src/pocketmine/item/Bucket.php +++ b/src/pocketmine/item/Bucket.php @@ -62,8 +62,8 @@ class Bucket extends Item implements MaybeConsumable{ $ev = new PlayerBucketFillEvent($player, $blockReplace, $face, $this, $resultItem); $ev->call(); if(!$ev->isCancelled()){ - $player->getLevel()->setBlock($blockClicked, BlockFactory::get(Block::AIR), true, true); - $player->getLevel()->broadcastLevelSoundEvent($blockClicked->add(0.5, 0.5, 0.5), $blockClicked->getBucketFillSound()); + $player->getLevelNonNull()->setBlock($blockClicked, BlockFactory::get(Block::AIR), true, true); + $player->getLevelNonNull()->broadcastLevelSoundEvent($blockClicked->add(0.5, 0.5, 0.5), $blockClicked->getBucketFillSound()); if($player->isSurvival()){ if($stack->getCount() === 0){ $player->getInventory()->setItemInHand($ev->getItem()); @@ -84,8 +84,8 @@ class Bucket extends Item implements MaybeConsumable{ $ev = new PlayerBucketEmptyEvent($player, $blockReplace, $face, $this, ItemFactory::get(Item::BUCKET)); $ev->call(); if(!$ev->isCancelled()){ - $player->getLevel()->setBlock($blockReplace, $resultBlock->getFlowingForm(), true, true); - $player->getLevel()->broadcastLevelSoundEvent($blockReplace->add(0.5, 0.5, 0.5), $resultBlock->getBucketEmptySound()); + $player->getLevelNonNull()->setBlock($blockReplace, $resultBlock->getFlowingForm(), true, true); + $player->getLevelNonNull()->broadcastLevelSoundEvent($blockReplace->add(0.5, 0.5, 0.5), $resultBlock->getBucketEmptySound()); if($player->isSurvival()){ $player->getInventory()->setItemInHand($ev->getItem()); diff --git a/src/pocketmine/item/ChorusFruit.php b/src/pocketmine/item/ChorusFruit.php index f6744a167..d66192aa8 100644 --- a/src/pocketmine/item/ChorusFruit.php +++ b/src/pocketmine/item/ChorusFruit.php @@ -50,11 +50,10 @@ class ChorusFruit extends Food{ } public function onConsume(Living $consumer){ - $level = $consumer->getLevel(); - assert($level !== null); + $level = $consumer->getLevelNonNull(); $minX = $consumer->getFloorX() - 8; - $minY = min($consumer->getFloorY(), $consumer->getLevel()->getWorldHeight()) - 8; + $minY = min($consumer->getFloorY(), $consumer->getLevelNonNull()->getWorldHeight()) - 8; $minZ = $consumer->getFloorZ() - 8; $maxX = $minX + 16; diff --git a/src/pocketmine/item/FlintSteel.php b/src/pocketmine/item/FlintSteel.php index 4afd59263..49aa84e54 100644 --- a/src/pocketmine/item/FlintSteel.php +++ b/src/pocketmine/item/FlintSteel.php @@ -28,7 +28,6 @@ use pocketmine\block\BlockFactory; use pocketmine\math\Vector3; use pocketmine\network\mcpe\protocol\LevelSoundEventPacket; use pocketmine\Player; -use function assert; class FlintSteel extends Tool{ public function __construct(int $meta = 0){ @@ -37,8 +36,7 @@ class FlintSteel extends Tool{ public function onActivate(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : bool{ if($blockReplace->getId() === self::AIR){ - $level = $player->getLevel(); - assert($level !== null); + $level = $player->getLevelNonNull(); $level->setBlock($blockReplace, BlockFactory::get(Block::FIRE), true); $level->broadcastLevelSoundEvent($blockReplace->add(0.5, 0.5, 0.5), LevelSoundEventPacket::SOUND_IGNITE); diff --git a/src/pocketmine/item/Item.php b/src/pocketmine/item/Item.php index 5edd234e8..83be88cf9 100644 --- a/src/pocketmine/item/Item.php +++ b/src/pocketmine/item/Item.php @@ -852,7 +852,7 @@ class Item implements ItemIds, \JsonSerializable{ $item = ItemFactory::get($idTag->getValue(), $meta, $count); }elseif($idTag instanceof StringTag){ //PC item save format try{ - $item = ItemFactory::fromString($idTag->getValue()); + $item = ItemFactory::fromStringSingle($idTag->getValue()); }catch(\InvalidArgumentException $e){ //TODO: improve error handling return ItemFactory::get(Item::AIR, 0, 0); diff --git a/src/pocketmine/item/ItemFactory.php b/src/pocketmine/item/ItemFactory.php index cf1c63774..2d537231f 100644 --- a/src/pocketmine/item/ItemFactory.php +++ b/src/pocketmine/item/ItemFactory.php @@ -354,32 +354,36 @@ class ItemFactory{ if($multiple){ $blocks = []; foreach(explode(",", $str) as $b){ - $blocks[] = self::fromString($b, false); + $blocks[] = self::fromStringSingle($b); } return $blocks; }else{ - $b = explode(":", str_replace([" ", "minecraft:"], ["_", ""], trim($str))); - if(!isset($b[1])){ - $meta = 0; - }elseif(is_numeric($b[1])){ - $meta = (int) $b[1]; - }else{ - throw new \InvalidArgumentException("Unable to parse \"" . $b[1] . "\" from \"" . $str . "\" as a valid meta value"); - } - - if(is_numeric($b[0])){ - $item = self::get((int) $b[0], $meta); - }elseif(defined(ItemIds::class . "::" . strtoupper($b[0]))){ - $item = self::get(constant(ItemIds::class . "::" . strtoupper($b[0])), $meta); - }else{ - throw new \InvalidArgumentException("Unable to resolve \"" . $str . "\" to a valid item"); - } - - return $item; + return self::fromStringSingle($str); } } + public static function fromStringSingle(string $str) : Item{ + $b = explode(":", str_replace([" ", "minecraft:"], ["_", ""], trim($str))); + if(!isset($b[1])){ + $meta = 0; + }elseif(is_numeric($b[1])){ + $meta = (int) $b[1]; + }else{ + throw new \InvalidArgumentException("Unable to parse \"" . $b[1] . "\" from \"" . $str . "\" as a valid meta value"); + } + + if(is_numeric($b[0])){ + $item = self::get((int) $b[0], $meta); + }elseif(defined(ItemIds::class . "::" . strtoupper($b[0]))){ + $item = self::get(constant(ItemIds::class . "::" . strtoupper($b[0])), $meta); + }else{ + throw new \InvalidArgumentException("Unable to resolve \"" . $str . "\" to a valid item"); + } + + return $item; + } + /** * Returns whether the specified item ID is already registered in the item factory. */ diff --git a/src/pocketmine/item/PaintingItem.php b/src/pocketmine/item/PaintingItem.php index 115dba494..90709223a 100644 --- a/src/pocketmine/item/PaintingItem.php +++ b/src/pocketmine/item/PaintingItem.php @@ -93,13 +93,13 @@ class PaintingItem extends Item{ $nbt->setInt("TileY", $blockClicked->getFloorY()); $nbt->setInt("TileZ", $blockClicked->getFloorZ()); - $entity = Entity::createEntity("Painting", $blockReplace->getLevel(), $nbt); + $entity = Entity::createEntity("Painting", $blockReplace->getLevelNonNull(), $nbt); if($entity instanceof Entity){ $this->pop(); $entity->spawnToAll(); - $player->getLevel()->broadcastLevelEvent($blockReplace->add(0.5, 0.5, 0.5), LevelEventPacket::EVENT_SOUND_ITEMFRAME_PLACE); //item frame and painting have the same sound + $player->getLevelNonNull()->broadcastLevelEvent($blockReplace->add(0.5, 0.5, 0.5), LevelEventPacket::EVENT_SOUND_ITEMFRAME_PLACE); //item frame and painting have the same sound return true; } diff --git a/src/pocketmine/item/ProjectileItem.php b/src/pocketmine/item/ProjectileItem.php index f0f0860cc..fccc4c3bf 100644 --- a/src/pocketmine/item/ProjectileItem.php +++ b/src/pocketmine/item/ProjectileItem.php @@ -49,7 +49,7 @@ abstract class ProjectileItem extends Item{ $nbt = Entity::createBaseNBT($player->add(0, $player->getEyeHeight(), 0), $directionVector, $player->yaw, $player->pitch); $this->addExtraTags($nbt); - $projectile = Entity::createEntity($this->getProjectileEntityType(), $player->getLevel(), $nbt, $player); + $projectile = Entity::createEntity($this->getProjectileEntityType(), $player->getLevelNonNull(), $nbt, $player); if($projectile !== null){ $projectile->setMotion($projectile->getMotion()->multiply($this->getThrowForce())); } @@ -64,7 +64,7 @@ abstract class ProjectileItem extends Item{ }else{ $projectile->spawnToAll(); - $player->getLevel()->broadcastLevelSoundEvent($player, LevelSoundEventPacket::SOUND_THROW, 0, EntityIds::PLAYER); + $player->getLevelNonNull()->broadcastLevelSoundEvent($player, LevelSoundEventPacket::SOUND_THROW, 0, EntityIds::PLAYER); } }elseif($projectile !== null){ $projectile->spawnToAll(); diff --git a/src/pocketmine/item/SpawnEgg.php b/src/pocketmine/item/SpawnEgg.php index 92ac5d4a1..42400b0cd 100644 --- a/src/pocketmine/item/SpawnEgg.php +++ b/src/pocketmine/item/SpawnEgg.php @@ -41,7 +41,7 @@ class SpawnEgg extends Item{ $nbt->setString("CustomName", $this->getCustomName()); } - $entity = Entity::createEntity($this->meta, $player->getLevel(), $nbt); + $entity = Entity::createEntity($this->meta, $player->getLevelNonNull(), $nbt); if($entity instanceof Entity){ $this->pop(); diff --git a/src/pocketmine/item/enchantment/Enchantment.php b/src/pocketmine/item/enchantment/Enchantment.php index 4ba5e5d32..8586cdc3e 100644 --- a/src/pocketmine/item/enchantment/Enchantment.php +++ b/src/pocketmine/item/enchantment/Enchantment.php @@ -66,6 +66,9 @@ class Enchantment{ public const RIPTIDE = 30; public const LOYALTY = 31; public const CHANNELING = 32; + public const MULTISHOT = 33; + public const PIERCING = 34; + public const QUICK_CHARGE = 35; public const RARITY_COMMON = 10; public const RARITY_UNCOMMON = 5; diff --git a/src/pocketmine/level/Explosion.php b/src/pocketmine/level/Explosion.php index 4af974365..72101a075 100644 --- a/src/pocketmine/level/Explosion.php +++ b/src/pocketmine/level/Explosion.php @@ -74,7 +74,7 @@ class Explosion{ throw new \InvalidArgumentException("Position does not have a valid world"); } $this->source = $center; - $this->level = $center->getLevel(); + $this->level = $center->getLevelNonNull(); if($size <= 0){ throw new \InvalidArgumentException("Explosion radius must be greater than 0, got $size"); diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index 44476d9f4..485bd471c 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -128,9 +128,11 @@ class Level implements ChunkManager, Metadatable{ public const Y_MASK = 0xFF; public const Y_MAX = 0x100; //256 - public const TIME_DAY = 0; + public const TIME_DAY = 1000; + public const TIME_NOON = 6000; public const TIME_SUNSET = 12000; - public const TIME_NIGHT = 14000; + public const TIME_NIGHT = 13000; + public const TIME_MIDNIGHT = 18000; public const TIME_SUNRISE = 23000; public const TIME_FULL = 24000; @@ -935,7 +937,7 @@ class Level implements ChunkManager, Metadatable{ } if($resetTime){ - $time = $this->getTime() % Level::TIME_FULL; + $time = $this->getTimeOfDay(); if($time >= Level::TIME_NIGHT and $time < Level::TIME_SUNRISE){ $this->setTime($this->getTime() + Level::TIME_FULL - $time); @@ -1764,7 +1766,7 @@ class Level implements ChunkManager, Metadatable{ if($tag instanceof ListTag){ foreach($tag as $v){ if($v instanceof StringTag){ - $entry = ItemFactory::fromString($v->getValue()); + $entry = ItemFactory::fromStringSingle($v->getValue()); if($entry->getId() > 0 and $entry->getBlock()->getId() === $target->getId()){ $canBreak = true; break; @@ -1895,15 +1897,6 @@ class Level implements ChunkManager, Metadatable{ if(count($this->getCollidingEntities($collisionBox)) > 0){ return false; //Entity in block } - - if($player !== null){ - if(($diff = $player->getNextPosition()->subtract($player->getPosition())) and $diff->lengthSquared() > 0.00001){ - $bb = $player->getBoundingBox()->offsetCopy($diff->x, $diff->y, $diff->z); - if($collisionBox->intersectsWith($bb)){ - return false; //Inside player BB - } - } - } } } @@ -1919,7 +1912,7 @@ class Level implements ChunkManager, Metadatable{ if($tag instanceof ListTag){ foreach($tag as $v){ if($v instanceof StringTag){ - $entry = ItemFactory::fromString($v->getValue()); + $entry = ItemFactory::fromStringSingle($v->getValue()); if($entry->getId() > 0 and $entry->getBlock()->getId() === $blockClicked->getId()){ $canPlace = true; break; @@ -2840,6 +2833,13 @@ class Level implements ChunkManager, Metadatable{ return $this->time; } + /** + * Returns the current time of day + */ + public function getTimeOfDay() : int{ + return $this->time % self::TIME_FULL; + } + /** * Returns the Level name */ diff --git a/src/pocketmine/level/Location.php b/src/pocketmine/level/Location.php index a3394e31a..15362ac3e 100644 --- a/src/pocketmine/level/Location.php +++ b/src/pocketmine/level/Location.php @@ -75,7 +75,7 @@ class Location extends Position{ } public function __toString(){ - return "Location (level=" . ($this->isValid() ? $this->getLevel()->getName() : "null") . ", x=$this->x, y=$this->y, z=$this->z, yaw=$this->yaw, pitch=$this->pitch)"; + return "Location (level=" . ($this->isValid() ? $this->getLevelNonNull()->getName() : "null") . ", x=$this->x, y=$this->y, z=$this->z, yaw=$this->yaw, pitch=$this->pitch)"; } public function equals(Vector3 $v) : bool{ diff --git a/src/pocketmine/level/Position.php b/src/pocketmine/level/Position.php index 19e4fcdb5..29832f54d 100644 --- a/src/pocketmine/level/Position.php +++ b/src/pocketmine/level/Position.php @@ -25,6 +25,7 @@ namespace pocketmine\level; use pocketmine\math\Vector3; use pocketmine\utils\MainLogger; +use pocketmine\utils\AssumptionFailedError; use function assert; class Position extends Vector3{ @@ -71,6 +72,19 @@ class Position extends Vector3{ return $this->level; } + /** + * Returns the position's world if valid. Throws an error if the world is unexpectedly null. + * + * @throws AssumptionFailedError + */ + public function getLevelNonNull() : Level{ + $world = $this->getLevel(); + if($world === null){ + throw new AssumptionFailedError("Position world is null"); + } + return $world; + } + /** * Sets the target Level of the position. * @@ -112,7 +126,7 @@ class Position extends Vector3{ } public function __toString(){ - return "Position(level=" . ($this->isValid() ? $this->getLevel()->getName() : "null") . ",x=" . $this->x . ",y=" . $this->y . ",z=" . $this->z . ")"; + return "Position(level=" . ($this->isValid() ? $this->getLevelNonNull()->getName() : "null") . ",x=" . $this->x . ",y=" . $this->y . ",z=" . $this->z . ")"; } public function equals(Vector3 $v) : bool{ diff --git a/src/pocketmine/level/format/io/region/RegionLoader.php b/src/pocketmine/level/format/io/region/RegionLoader.php index 87383a3a6..e86d3c56f 100644 --- a/src/pocketmine/level/format/io/region/RegionLoader.php +++ b/src/pocketmine/level/format/io/region/RegionLoader.php @@ -25,6 +25,7 @@ namespace pocketmine\level\format\io\region; use pocketmine\level\format\ChunkException; use pocketmine\level\format\io\exception\CorruptedChunkException; +use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Binary; use pocketmine\utils\MainLogger; use function ceil; @@ -98,7 +99,9 @@ class RegionLoader{ throw new CorruptedRegionException("Region file should be padded to a multiple of 4KiB"); } - $this->filePointer = fopen($this->filePath, "r+b"); + $filePointer = fopen($this->filePath, "r+b"); + if($filePointer === false) throw new AssumptionFailedError("fopen() should not fail here"); + $this->filePointer = $filePointer; stream_set_read_buffer($this->filePointer, 1024 * 16); //16KB stream_set_write_buffer($this->filePointer, 1024 * 16); //16KB if(!$exists){ diff --git a/src/pocketmine/level/generator/Flat.php b/src/pocketmine/level/generator/Flat.php index 24c4888d3..681b4ad0a 100644 --- a/src/pocketmine/level/generator/Flat.php +++ b/src/pocketmine/level/generator/Flat.php @@ -120,7 +120,7 @@ class Flat extends Generator{ $cnt = $matches[1] !== "" ? (int) $matches[1] : 1; try{ - $b = ItemFactory::fromString($matches[2])->getBlock(); + $b = ItemFactory::fromStringSingle($matches[2])->getBlock(); }catch(\InvalidArgumentException $e){ throw new InvalidGeneratorOptionsException("Invalid preset layer \"$line\": " . $e->getMessage(), 0, $e); } diff --git a/src/pocketmine/level/particle/EnchantParticle.php b/src/pocketmine/level/particle/EnchantParticle.php index ca2e599ce..c58541214 100644 --- a/src/pocketmine/level/particle/EnchantParticle.php +++ b/src/pocketmine/level/particle/EnchantParticle.php @@ -24,9 +24,10 @@ declare(strict_types=1); namespace pocketmine\level\particle; use pocketmine\math\Vector3; +use pocketmine\utils\Color; class EnchantParticle extends GenericParticle{ - public function __construct(Vector3 $pos){ - parent::__construct($pos, Particle::TYPE_MOB_SPELL); + public function __construct(Vector3 $pos, ?Color $color = null){ + parent::__construct($pos, Particle::TYPE_MOB_SPELL, $color !== null ? $color->toARGB() : 0); } } diff --git a/src/pocketmine/level/particle/InstantEnchantParticle.php b/src/pocketmine/level/particle/InstantEnchantParticle.php index 337a89ff3..30d0c87e5 100644 --- a/src/pocketmine/level/particle/InstantEnchantParticle.php +++ b/src/pocketmine/level/particle/InstantEnchantParticle.php @@ -24,9 +24,10 @@ declare(strict_types=1); namespace pocketmine\level\particle; use pocketmine\math\Vector3; +use pocketmine\utils\Color; class InstantEnchantParticle extends GenericParticle{ - public function __construct(Vector3 $pos){ - parent::__construct($pos, Particle::TYPE_MOB_SPELL_INSTANTANEOUS); + public function __construct(Vector3 $pos, ?Color $color = null){ + parent::__construct($pos, Particle::TYPE_MOB_SPELL_INSTANTANEOUS, $color !== null ? $color->toARGB() : 0); } } diff --git a/src/pocketmine/network/mcpe/RakLibInterface.php b/src/pocketmine/network/mcpe/RakLibInterface.php index c9d28370c..d8a67bfdf 100644 --- a/src/pocketmine/network/mcpe/RakLibInterface.php +++ b/src/pocketmine/network/mcpe/RakLibInterface.php @@ -226,6 +226,10 @@ class RakLibInterface implements ServerInstance, AdvancedSourceInterface{ $this->interface->sendOption("portChecking", $name); } + public function setPacketLimit(int $limit) : void{ + $this->interface->sendOption("packetLimit", $limit); + } + public function handleOption(string $option, string $value) : void{ if($option === "bandwidth"){ $v = unserialize($value); diff --git a/src/pocketmine/network/mcpe/protocol/AddActorPacket.php b/src/pocketmine/network/mcpe/protocol/AddActorPacket.php index e3122dc05..fd4802b68 100644 --- a/src/pocketmine/network/mcpe/protocol/AddActorPacket.php +++ b/src/pocketmine/network/mcpe/protocol/AddActorPacket.php @@ -30,7 +30,6 @@ use pocketmine\entity\EntityIds; use pocketmine\math\Vector3; use pocketmine\network\mcpe\NetworkSession; use pocketmine\network\mcpe\protocol\types\EntityLink; -use function array_search; use function count; class AddActorPacket extends DataPacket{ @@ -148,7 +147,7 @@ class AddActorPacket extends DataPacket{ public $entityUniqueId = null; //TODO /** @var int */ public $entityRuntimeId; - /** @var int */ + /** @var string */ public $type; /** @var Vector3 */ public $position; @@ -174,10 +173,7 @@ class AddActorPacket extends DataPacket{ protected function decodePayload(){ $this->entityUniqueId = $this->getEntityUniqueId(); $this->entityRuntimeId = $this->getEntityRuntimeId(); - $this->type = array_search($t = $this->getString(), self::LEGACY_ID_MAP_BC, true); - if($this->type === false){ - throw new \UnexpectedValueException("Can't map ID $t to legacy ID"); - } + $this->type = $this->getString(); $this->position = $this->getVector3(); $this->motion = $this->getVector3(); $this->pitch = $this->getLFloat(); @@ -212,10 +208,7 @@ class AddActorPacket extends DataPacket{ protected function encodePayload(){ $this->putEntityUniqueId($this->entityUniqueId ?? $this->entityRuntimeId); $this->putEntityRuntimeId($this->entityRuntimeId); - if(!isset(self::LEGACY_ID_MAP_BC[$this->type])){ - throw new \InvalidArgumentException("Unknown entity numeric ID $this->type"); - } - $this->putString(self::LEGACY_ID_MAP_BC[$this->type]); + $this->putString($this->type); $this->putVector3($this->position); $this->putVector3Nullable($this->motion); $this->putLFloat($this->pitch); diff --git a/src/pocketmine/network/mcpe/protocol/types/WindowTypes.php b/src/pocketmine/network/mcpe/protocol/types/WindowTypes.php index b29628ac9..afa36f5a9 100644 --- a/src/pocketmine/network/mcpe/protocol/types/WindowTypes.php +++ b/src/pocketmine/network/mcpe/protocol/types/WindowTypes.php @@ -51,9 +51,12 @@ interface WindowTypes{ public const ELEMENT_CONSTRUCTOR = 21; public const MATERIAL_REDUCER = 22; public const LAB_TABLE = 23; - + public const LOOM = 24; + public const LECTERN = 25; + public const GRINDSTONE = 26; public const BLAST_FURNACE = 27; public const SMOKER = 28; public const STONECUTTER = 29; + public const CARTOGRAPHY = 30; } diff --git a/src/pocketmine/network/rcon/RCON.php b/src/pocketmine/network/rcon/RCON.php index 61a123e37..e0522de45 100644 --- a/src/pocketmine/network/rcon/RCON.php +++ b/src/pocketmine/network/rcon/RCON.php @@ -75,13 +75,17 @@ class RCON{ throw new \InvalidArgumentException("Empty password"); } - $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); + $socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP); + if($socket === false){ + throw new \RuntimeException("Failed to create socket:" . trim(socket_strerror(socket_last_error()))); + } + $this->socket = $socket; if(!socket_set_option($this->socket, SOL_SOCKET, SO_REUSEADDR, 1)){ throw new \RuntimeException("Unable to set option on socket: " . trim(socket_strerror(socket_last_error()))); } - if($this->socket === false or !@socket_bind($this->socket, $interface, $port) or !@socket_listen($this->socket, 5)){ + if(!@socket_bind($this->socket, $interface, $port) or !@socket_listen($this->socket, 5)){ throw new \RuntimeException(trim(socket_strerror(socket_last_error()))); } diff --git a/src/pocketmine/network/rcon/RCONInstance.php b/src/pocketmine/network/rcon/RCONInstance.php index 73999cb0d..05aa0a5ac 100644 --- a/src/pocketmine/network/rcon/RCONInstance.php +++ b/src/pocketmine/network/rcon/RCONInstance.php @@ -167,7 +167,7 @@ class RCONInstance extends Thread{ /** @var resource[] $clients */ $clients = []; - /** @var int[] $authenticated */ + /** @var bool[] $authenticated */ $authenticated = []; /** @var float[] $timeouts */ $timeouts = []; diff --git a/src/pocketmine/network/upnp/UPnP.php b/src/pocketmine/network/upnp/UPnP.php index 243300879..6fe5a6951 100644 --- a/src/pocketmine/network/upnp/UPnP.php +++ b/src/pocketmine/network/upnp/UPnP.php @@ -37,7 +37,7 @@ abstract class UPnP{ if(!Internet::$online){ throw new \RuntimeException("Server is offline"); } - if(Utils::getOS() !== "win"){ + if(Utils::getOS() !== Utils::OS_WINDOWS){ throw new \RuntimeException("UPnP is only supported on Windows"); } if(!class_exists("COM")){ @@ -62,7 +62,7 @@ abstract class UPnP{ if(!Internet::$online){ return false; } - if(Utils::getOS() != "win" or !class_exists("COM")){ + if(Utils::getOS() !== Utils::OS_WINDOWS or !class_exists("COM")){ return false; } diff --git a/src/pocketmine/plugin/PluginBase.php b/src/pocketmine/plugin/PluginBase.php index 08ecd936e..a15a7db9b 100644 --- a/src/pocketmine/plugin/PluginBase.php +++ b/src/pocketmine/plugin/PluginBase.php @@ -28,6 +28,7 @@ use pocketmine\command\CommandSender; use pocketmine\command\PluginIdentifiableCommand; use pocketmine\scheduler\TaskScheduler; use pocketmine\Server; +use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Config; use function dirname; use function fclose; @@ -171,7 +172,9 @@ abstract class PluginBase implements Plugin{ public function getResource(string $filename){ $filename = rtrim(str_replace(DIRECTORY_SEPARATOR, "/", $filename), "/"); if(file_exists($this->file . "resources/" . $filename)){ - return fopen($this->file . "resources/" . $filename, "rb"); + $resource = fopen($this->file . "resources/" . $filename, "rb"); + if($resource === false) throw new AssumptionFailedError("fopen() should not fail on a file which exists"); + return $resource; } return null; @@ -195,7 +198,10 @@ abstract class PluginBase implements Plugin{ return false; } - $ret = stream_copy_to_stream($resource, $fp = fopen($out, "wb")) > 0; + $fp = fopen($out, "wb"); + if($fp === false) throw new AssumptionFailedError("fopen() should not fail with wb flags"); + + $ret = stream_copy_to_stream($resource, $fp) > 0; fclose($fp); fclose($resource); return $ret; diff --git a/src/pocketmine/plugin/PluginDescription.php b/src/pocketmine/plugin/PluginDescription.php index cc321cbf6..10db86877 100644 --- a/src/pocketmine/plugin/PluginDescription.php +++ b/src/pocketmine/plugin/PluginDescription.php @@ -28,7 +28,6 @@ use function array_map; use function array_values; use function constant; use function defined; -use function extension_loaded; use function is_array; use function phpversion; use function preg_match; @@ -55,6 +54,8 @@ class PluginDescription{ private $api; /** @var int[] */ private $compatibleMcpeProtocols = []; + /** @var string[] */ + private $compatibleOperatingSystems = []; /** * @var string[][] * @phpstan-var array> @@ -114,6 +115,7 @@ class PluginDescription{ $this->api = array_map("\strval", (array) ($plugin["api"] ?? [])); $this->compatibleMcpeProtocols = array_map("\intval", (array) ($plugin["mcpe-protocol"] ?? [])); + $this->compatibleOperatingSystems = array_map("\strval", (array) ($plugin["os"] ?? [])); if(isset($plugin["commands"]) and is_array($plugin["commands"])){ $this->commands = $plugin["commands"]; @@ -185,6 +187,13 @@ class PluginDescription{ return $this->compatibleMcpeProtocols; } + /** + * @return string[] + */ + public function getCompatibleOperatingSystems() : array{ + return $this->compatibleOperatingSystems; + } + /** * @return string[] */ @@ -220,11 +229,11 @@ class PluginDescription{ */ public function checkRequiredExtensions(){ foreach($this->extensions as $name => $versionConstrs){ - if(!extension_loaded($name)){ + $gotVersion = phpversion($name); + if($gotVersion === false){ throw new PluginException("Required extension $name not loaded"); } - $gotVersion = phpversion($name); foreach($versionConstrs as $constr){ // versionConstrs_loop if($constr === "*"){ continue; diff --git a/src/pocketmine/plugin/PluginManager.php b/src/pocketmine/plugin/PluginManager.php index 78cbdc66e..562657f36 100644 --- a/src/pocketmine/plugin/PluginManager.php +++ b/src/pocketmine/plugin/PluginManager.php @@ -37,6 +37,7 @@ use pocketmine\permission\Permission; use pocketmine\permission\PermissionManager; use pocketmine\Server; use pocketmine\timings\TimingsHandler; +use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Utils; use function array_intersect; use function array_map; @@ -50,6 +51,7 @@ use function file_exists; use function get_class; use function gettype; use function implode; +use function in_array; use function is_a; use function is_array; use function is_bool; @@ -228,6 +230,7 @@ class PluginManager{ shuffle($files); //this prevents plugins implicitly relying on the filesystem name order when they should be using dependency properties foreach($loaders as $loader){ foreach($files as $file){ + if(!is_string($file)) throw new AssumptionFailedError("FilesystemIterator current should be string when using CURRENT_AS_PATHNAME"); if(!$loader->canLoadPlugin($file)){ continue; } @@ -258,6 +261,14 @@ class PluginManager{ continue; } + if(count($description->getCompatibleOperatingSystems()) > 0 and !in_array(Utils::getOS(), $description->getCompatibleOperatingSystems(), true)) { + $this->server->getLogger()->error($this->server->getLanguage()->translateString("pocketmine.plugin.loadError", [ + $name, + $this->server->getLanguage()->translateString("%pocketmine.plugin.incompatibleOS", [implode(", ", $description->getCompatibleOperatingSystems())]) + ])); + continue; + } + if(count($pluginMcpeProtocols = $description->getCompatibleMcpeProtocols()) > 0){ $serverMcpeProtocols = [ProtocolInfo::CURRENT_PROTOCOL]; if(count(array_intersect($pluginMcpeProtocols, $serverMcpeProtocols)) === 0){ @@ -742,15 +753,6 @@ class PluginManager{ throw new PluginException($event . " is not an Event"); } - $tags = Utils::parseDocComment((string) (new \ReflectionClass($event))->getDocComment()); - if(isset($tags["deprecated"]) and $this->server->getProperty("settings.deprecated-verbose", true)){ - $this->server->getLogger()->warning($this->server->getLanguage()->translateString("pocketmine.plugin.deprecatedEvent", [ - $plugin->getName(), - $event, - get_class($listener) . "->" . ($executor instanceof MethodEventExecutor ? $executor->getMethod() : "") - ])); - } - if(!$plugin->isEnabled()){ throw new PluginException("Plugin attempted to register " . $event . " while not enabled"); } diff --git a/src/pocketmine/plugin/ScriptPluginLoader.php b/src/pocketmine/plugin/ScriptPluginLoader.php index 126f66de2..ea9d4c22d 100644 --- a/src/pocketmine/plugin/ScriptPluginLoader.php +++ b/src/pocketmine/plugin/ScriptPluginLoader.php @@ -55,7 +55,10 @@ class ScriptPluginLoader implements PluginLoader{ * Gets the PluginDescription from the file */ public function getPluginDescription(string $file) : ?PluginDescription{ - $content = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + $content = @file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + if($content === false){ + return null; + } $data = []; diff --git a/src/pocketmine/resources/pocketmine.yml b/src/pocketmine/resources/pocketmine.yml index 01618c2c5..3a0225537 100644 --- a/src/pocketmine/resources/pocketmine.yml +++ b/src/pocketmine/resources/pocketmine.yml @@ -9,8 +9,6 @@ settings: shutdown-message: "Server closed" #Allow listing plugins via Query query-plugins: true - #Show a console message when a plugin uses deprecated API methods - deprecated-verbose: true #Enable plugin and core profiling by default enable-profiling: false #Will only add results when tick measurement is below or equal to given value (default 20) diff --git a/src/pocketmine/scheduler/AsyncPool.php b/src/pocketmine/scheduler/AsyncPool.php index 4aca3a5d9..f5a0576f1 100644 --- a/src/pocketmine/scheduler/AsyncPool.php +++ b/src/pocketmine/scheduler/AsyncPool.php @@ -28,7 +28,6 @@ use pocketmine\utils\Utils; use function array_keys; use function assert; use function count; -use function get_class; use function spl_object_hash; use function time; use const PHP_INT_MAX; @@ -54,18 +53,33 @@ class AsyncPool{ /** @var int */ private $workerMemoryLimit; - /** @var AsyncTask[] */ + /** + * @var AsyncTask[] + * @phpstan-var array + */ private $tasks = []; - /** @var int[] */ + /** + * @var int[] + * @phpstan-var array + */ private $taskWorkers = []; /** @var int */ private $nextTaskId = 1; - /** @var AsyncWorker[] */ + /** + * @var AsyncWorker[] + * @phpstan-var array + */ private $workers = []; - /** @var int[] */ + /** + * @var int[] + * @phpstan-var array + */ private $workerUsage = []; - /** @var int[] */ + /** + * @var int[] + * @phpstan-var array + */ private $workerLastUsed = []; /** @@ -163,13 +177,14 @@ class AsyncPool{ } $task->progressUpdates = new \Threaded; - $task->setTaskId($this->nextTaskId++); + $taskId = $this->nextTaskId++; + $task->setTaskId($taskId); - $this->tasks[$task->getTaskId()] = $task; + $this->tasks[$taskId] = $task; $this->getWorker($worker)->stack($task); $this->workerUsage[$worker]++; - $this->taskWorkers[$task->getTaskId()] = $worker; + $this->taskWorkers[$taskId] = $worker; $this->workerLastUsed[$worker] = time(); } @@ -302,9 +317,6 @@ class AsyncPool{ */ $task->checkProgressUpdates($this->server); $task->onCompletion($this->server); - if($task->removeDanglingStoredObjects()){ - $this->logger->notice("AsyncTask " . get_class($task) . " stored local complex data but did not remove them after completion"); - } } $this->removeTask($task); diff --git a/src/pocketmine/scheduler/AsyncTask.php b/src/pocketmine/scheduler/AsyncTask.php index dc4723c58..00b906526 100644 --- a/src/pocketmine/scheduler/AsyncTask.php +++ b/src/pocketmine/scheduler/AsyncTask.php @@ -25,6 +25,7 @@ namespace pocketmine\scheduler; use pocketmine\Collectable; use pocketmine\Server; +use pocketmine\utils\AssumptionFailedError; use function is_scalar; use function serialize; use function unserialize; @@ -51,7 +52,7 @@ abstract class AsyncTask extends Collectable{ * @var \SplObjectStorage|null * Used to store objects on the main thread which should not be serialized. */ - private static $localObjectStorage; + private static $threadLocalStorage; /** @var AsyncWorker|null $worker */ public $worker = null; @@ -97,7 +98,11 @@ abstract class AsyncTask extends Collectable{ * @return mixed */ public function getResult(){ - return $this->serialized ? unserialize($this->result) : $this->result; + if($this->serialized){ + if(!is_string($this->result)) throw new AssumptionFailedError("Result expected to be a serialized string"); + return unserialize($this->result); + } + return $this->result; } /** @@ -139,6 +144,7 @@ abstract class AsyncTask extends Collectable{ } /** + * @deprecated * @see AsyncWorker::getFromThreadStore() * * @return mixed @@ -151,6 +157,7 @@ abstract class AsyncTask extends Collectable{ } /** + * @deprecated * @see AsyncWorker::saveToThreadStore() * * @param mixed $value @@ -165,6 +172,7 @@ abstract class AsyncTask extends Collectable{ } /** + * @deprecated * @see AsyncWorker::removeFromThreadStore() */ public function removeFromThreadStore(string $identifier) : void{ @@ -236,9 +244,6 @@ abstract class AsyncTask extends Collectable{ * * Scalar types can be stored directly in class properties instead of using this storage. * - * Objects stored in this storage MUST be retrieved through {@link AsyncTask::fetchLocal} when {@link AsyncTask::onCompletion} is called. - * Otherwise, a NOTICE level message will be raised and the reference will be removed after onCompletion exits. - * * WARNING: THIS METHOD SHOULD ONLY BE CALLED FROM THE MAIN THREAD! * * @param mixed $complexData the data to store @@ -251,26 +256,19 @@ abstract class AsyncTask extends Collectable{ throw new \BadMethodCallException("Objects can only be stored from the parent thread"); } - if(self::$localObjectStorage === null){ - self::$localObjectStorage = new \SplObjectStorage(); //lazy init + if(self::$threadLocalStorage === null){ + self::$threadLocalStorage = new \SplObjectStorage(); //lazy init } - if(isset(self::$localObjectStorage[$this])){ + if(isset(self::$threadLocalStorage[$this])){ throw new \InvalidStateException("Already storing complex data for this async task"); } - self::$localObjectStorage[$this] = $complexData; + self::$threadLocalStorage[$this] = $complexData; } /** - * Returns and removes mixed data in thread-local storage on the parent thread. Call this method from - * {@link AsyncTask::onCompletion} to fetch the data stored in the object store, if any. - * - * If no data was stored in the local store, or if the data was already retrieved by a previous call to fetchLocal, - * do NOT call this method, or an exception will be thrown. - * - * Do not call this method from {@link AsyncTask::onProgressUpdate}, because this method deletes stored data, which - * means that you will not be able to retrieve it again afterwards. Use {@link AsyncTask::peekLocal} instead to - * retrieve stored data without removing it from the store. + * Returns data previously stored in thread-local storage on the parent thread. Use this during progress updates or + * task completion to retrieve data you stored using {@link AsyncTask::storeLocal}. * * WARNING: THIS METHOD SHOULD ONLY BE CALLED FROM THE MAIN THREAD! * @@ -280,25 +278,20 @@ abstract class AsyncTask extends Collectable{ * @throws \BadMethodCallException if called from any thread except the main thread */ protected function fetchLocal(){ - try{ - return $this->peekLocal(); - }finally{ - if(self::$localObjectStorage !== null){ - unset(self::$localObjectStorage[$this]); - } + if($this->worker !== null and $this->worker === \Thread::getCurrentThread()){ + throw new \BadMethodCallException("Objects can only be retrieved from the parent thread"); } + + if(self::$threadLocalStorage === null or !isset(self::$threadLocalStorage[$this])){ + throw new \InvalidStateException("No complex data stored for this async task"); + } + + return self::$threadLocalStorage[$this]; } /** - * Returns mixed data in thread-local storage on the parent thread **without clearing** it. Call this method from - * {@link AsyncTask::onProgressUpdate} to fetch the data stored if you need to be able to access the data later on, - * such as in another progress update. - * - * Use {@link AsyncTask::fetchLocal} instead from {@link AsyncTask::onCompletion}, because this method does not delete - * the data, and not clearing the data will result in a warning for memory leak after {@link AsyncTask::onCompletion} - * finished executing. - * - * WARNING: THIS METHOD SHOULD ONLY BE CALLED FROM THE MAIN THREAD! + * @deprecated + * @see AsyncTask::fetchLocal() * * @return mixed * @@ -306,26 +299,15 @@ abstract class AsyncTask extends Collectable{ * @throws \BadMethodCallException if called from any thread except the main thread */ protected function peekLocal(){ - if($this->worker !== null and $this->worker === \Thread::getCurrentThread()){ - throw new \BadMethodCallException("Objects can only be retrieved from the parent thread"); - } - - if(self::$localObjectStorage === null or !isset(self::$localObjectStorage[$this])){ - throw new \InvalidStateException("No complex data stored for this async task"); - } - - return self::$localObjectStorage[$this]; + return $this->fetchLocal(); } /** * @internal Called by the AsyncPool to destroy any leftover stored objects that this task failed to retrieve. */ - public function removeDanglingStoredObjects() : bool{ - if(self::$localObjectStorage !== null and isset(self::$localObjectStorage[$this])){ - unset(self::$localObjectStorage[$this]); - return true; + public function removeDanglingStoredObjects() : void{ + if(self::$threadLocalStorage !== null and isset(self::$threadLocalStorage[$this])){ + unset(self::$threadLocalStorage[$this]); } - - return false; } } diff --git a/src/pocketmine/scheduler/SendUsageTask.php b/src/pocketmine/scheduler/SendUsageTask.php index a43bcf5cf..7a722b6d0 100644 --- a/src/pocketmine/scheduler/SendUsageTask.php +++ b/src/pocketmine/scheduler/SendUsageTask.php @@ -25,7 +25,9 @@ namespace pocketmine\scheduler; use pocketmine\network\mcpe\protocol\ProtocolInfo; use pocketmine\Server; +use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Internet; +use pocketmine\utils\Process; use pocketmine\utils\Utils; use pocketmine\utils\UUID; use pocketmine\utils\VersionString; @@ -135,12 +137,12 @@ class SendUsageTask extends AsyncTask{ "historyList" => array_values($playerList) ]; - $info = Utils::getMemoryUsage(true); + $info = Process::getAdvancedMemoryUsage(); $data["system"] = [ "mainMemory" => $info[0], "totalMemory" => $info[1], "availableMemory" => $info[2], - "threadCount" => Utils::getThreadCount() + "threadCount" => Process::getThreadCount() ]; break; @@ -151,7 +153,9 @@ class SendUsageTask extends AsyncTask{ } $this->endpoint = $endpoint . "api/post"; - $this->data = json_encode($data/*, JSON_PRETTY_PRINT*/); + $data = json_encode($data/*, JSON_PRETTY_PRINT*/); + if($data === false) throw new AssumptionFailedError("Statistics JSON should never fail to encode: " . json_last_error_msg()); + $this->data = $data; } public function onRun(){ diff --git a/src/pocketmine/tile/Chest.php b/src/pocketmine/tile/Chest.php index 8066d69d9..41468fb1c 100644 --- a/src/pocketmine/tile/Chest.php +++ b/src/pocketmine/tile/Chest.php @@ -121,7 +121,7 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{ * @return void */ protected function checkPairing(){ - if($this->isPaired() and !$this->getLevel()->isInLoadedTerrain(new Vector3($this->pairX, $this->y, $this->pairZ))){ + if($this->isPaired() and !$this->getLevelNonNull()->isInLoadedTerrain(new Vector3($this->pairX, $this->y, $this->pairZ))){ //paired to a tile in an unloaded chunk $this->doubleInventory = null; @@ -160,7 +160,7 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{ public function getPair() : ?Chest{ if($this->isPaired()){ - $tile = $this->getLevel()->getTileAt($this->pairX, $this->y, $this->pairZ); + $tile = $this->getLevelNonNull()->getTileAt($this->pairX, $this->y, $this->pairZ); if($tile instanceof Chest){ return $tile; } diff --git a/src/pocketmine/tile/Furnace.php b/src/pocketmine/tile/Furnace.php index 984141c7a..920b93249 100644 --- a/src/pocketmine/tile/Furnace.php +++ b/src/pocketmine/tile/Furnace.php @@ -148,7 +148,7 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{ $this->maxTime = $this->burnTime = $ev->getBurnTime(); if($this->getBlock()->getId() === Block::FURNACE){ - $this->getLevel()->setBlock($this, BlockFactory::get(Block::BURNING_FURNACE, $this->getBlock()->getDamage()), true); + $this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::BURNING_FURNACE, $this->getBlock()->getDamage()), true); } if($this->burnTime > 0 and $ev->isBurning()){ @@ -211,7 +211,7 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{ $ret = true; }else{ if($this->getBlock()->getId() === Block::BURNING_FURNACE){ - $this->getLevel()->setBlock($this, BlockFactory::get(Block::FURNACE, $this->getBlock()->getDamage()), true); + $this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::FURNACE, $this->getBlock()->getDamage()), true); } $this->burnTime = $this->cookTime = $this->maxTime = 0; } diff --git a/src/pocketmine/tile/Spawnable.php b/src/pocketmine/tile/Spawnable.php index 6d416650a..41727905b 100644 --- a/src/pocketmine/tile/Spawnable.php +++ b/src/pocketmine/tile/Spawnable.php @@ -30,6 +30,7 @@ use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\StringTag; use pocketmine\network\mcpe\protocol\BlockActorDataPacket; use pocketmine\Player; +use pocketmine\utils\AssumptionFailedError; abstract class Spawnable extends Tile{ /** @var string|null */ @@ -96,7 +97,9 @@ abstract class Spawnable extends Tile{ self::$nbtWriter = new NetworkLittleEndianNBTStream(); } - $this->spawnCompoundCache = self::$nbtWriter->write($this->getSpawnCompound()); + $spawnCompoundCache = self::$nbtWriter->write($this->getSpawnCompound()); + if($spawnCompoundCache === false) throw new AssumptionFailedError("NBTStream->write() should not return false when given a CompoundTag"); + $this->spawnCompoundCache = $spawnCompoundCache; } return $this->spawnCompoundCache; diff --git a/src/pocketmine/tile/Tile.php b/src/pocketmine/tile/Tile.php index ac044a840..6ade66bb9 100644 --- a/src/pocketmine/tile/Tile.php +++ b/src/pocketmine/tile/Tile.php @@ -168,7 +168,7 @@ abstract class Tile extends Position{ parent::__construct($nbt->getInt(self::TAG_X), $nbt->getInt(self::TAG_Y), $nbt->getInt(self::TAG_Z), $level); $this->readSaveData($nbt); - $this->getLevel()->addTile($this); + $this->getLevelNonNull()->addTile($this); } public function getId() : int{ diff --git a/src/pocketmine/utils/AssumptionFailedError.php b/src/pocketmine/utils/AssumptionFailedError.php new file mode 100644 index 000000000..df7d2d464 --- /dev/null +++ b/src/pocketmine/utils/AssumptionFailedError.php @@ -0,0 +1,33 @@ +correct){ $content = file_get_contents($this->file); + if($content === false){ + $this->correct = false; + return false; + } $config = null; switch($this->type){ case Config::PROPERTIES: @@ -244,6 +248,13 @@ class Config{ } } + /** + * Returns the path of the config. + */ + public function getPath() : string{ + return $this->file; + } + /** * Sets the options for the JSON encoding when saving * @@ -562,7 +573,6 @@ class Config{ /** * @return mixed[] - * @phpstan-return array */ private function parseProperties(string $content) : array{ $result = []; diff --git a/src/pocketmine/utils/Internet.php b/src/pocketmine/utils/Internet.php index 9b85f0eff..729e57718 100644 --- a/src/pocketmine/utils/Internet.php +++ b/src/pocketmine/utils/Internet.php @@ -72,7 +72,7 @@ class Internet{ * * @param bool $force default false, force IP check even when cached * - * @return string|bool + * @return string|false */ public static function getIP(bool $force = false){ if(!self::$online){ @@ -116,7 +116,10 @@ class Internet{ * @throws InternetException */ public static function getInternalIP() : string{ - $sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); + $sock = @socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); + if($sock === false){ + throw new InternetException("Failed to get internal IP: " . trim(socket_strerror(socket_last_error()))); + } try{ if(!@socket_connect($sock, "8.8.8.8", 65534)){ throw new InternetException("Failed to get internal IP: " . trim(socket_strerror(socket_last_error($sock)))); @@ -205,6 +208,9 @@ class Internet{ } $ch = curl_init($page); + if($ch === false){ + throw new InternetException("Unable to create new cURL session"); + } curl_setopt_array($ch, $extraOpts + [ CURLOPT_SSL_VERIFYPEER => false, @@ -221,10 +227,10 @@ class Internet{ ]); try{ $raw = curl_exec($ch); - $error = curl_error($ch); - if($error !== ""){ - throw new InternetException($error); + if($raw === false){ + throw new InternetException(curl_error($ch)); } + if(!is_string($raw)) throw new AssumptionFailedError("curl_exec() should return string|false when CURLOPT_RETURNTRANSFER is set"); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $rawHeaders = substr($raw, 0, $headerSize); diff --git a/src/pocketmine/utils/MainLogger.php b/src/pocketmine/utils/MainLogger.php index 9c41de7a5..4bb3e2770 100644 --- a/src/pocketmine/utils/MainLogger.php +++ b/src/pocketmine/utils/MainLogger.php @@ -314,7 +314,7 @@ class MainLogger extends \AttachableThreadedLogger{ } $this->synchronized(function() use ($message, $level, $time) : void{ - echo Terminal::toANSI($message) . PHP_EOL; + Terminal::writeLine($message); foreach($this->attachments as $attachment){ $attachment->call($level, $message); diff --git a/src/pocketmine/utils/Process.php b/src/pocketmine/utils/Process.php new file mode 100644 index 000000000..aacdd4c33 --- /dev/null +++ b/src/pocketmine/utils/Process.php @@ -0,0 +1,178 @@ + 0){ + $VmRSS = ((int) $matches[1]) * 1024; + } + + if(preg_match("/VmSize:[ \t]+([0-9]+) kB/", $status, $matches) > 0){ + $VmSize = ((int) $matches[1]) * 1024; + } + } + + //TODO: more OS + + if($VmRSS === null){ + $VmRSS = memory_get_usage(); + } + + if($VmSize === null){ + $VmSize = memory_get_usage(true); + } + + return [$reserved, $VmRSS, $VmSize]; + } + + public static function getMemoryUsage() : int{ + return self::getAdvancedMemoryUsage()[1]; + } + + /** + * @return int[] + */ + public static function getRealMemoryUsage() : array{ + $stack = 0; + $heap = 0; + + if(Utils::getOS() === Utils::OS_LINUX or Utils::getOS() === Utils::OS_ANDROID){ + $mappings = @file("/proc/self/maps"); + if($mappings === false) throw new AssumptionFailedError("/proc/self/maps should always be accessible"); + foreach($mappings as $line){ + if(preg_match("#([a-z0-9]+)\\-([a-z0-9]+) [rwxp\\-]{4} [a-z0-9]+ [^\\[]*\\[([a-zA-z0-9]+)\\]#", trim($line), $matches) > 0){ + if(strpos($matches[3], "heap") === 0){ + $heap += (int) hexdec($matches[2]) - (int) hexdec($matches[1]); + }elseif(strpos($matches[3], "stack") === 0){ + $stack += (int) hexdec($matches[2]) - (int) hexdec($matches[1]); + } + } + } + } + + return [$heap, $stack]; + } + + public static function getThreadCount() : int{ + if(Utils::getOS() === Utils::OS_LINUX or Utils::getOS() === Utils::OS_ANDROID){ + $status = @file_get_contents("/proc/self/status"); + if($status === false) throw new AssumptionFailedError("/proc/self/status should always be accessible"); + if(preg_match("/Threads:[ \t]+([0-9]+)/", $status, $matches) > 0){ + return (int) $matches[1]; + } + } + + //TODO: more OS + + return count(ThreadManager::getInstance()->getAll()) + 3; //RakLib + MainLogger + Main Thread + } + + /** + * @param int $pid + */ + public static function kill($pid) : void{ + if(MainLogger::isRegisteredStatic()){ + MainLogger::getLogger()->syncFlushBuffer(); + } + switch(Utils::getOS()){ + case Utils::OS_WINDOWS: + exec("taskkill.exe /F /PID $pid > NUL"); + break; + case Utils::OS_MACOS: + case Utils::OS_LINUX: + default: + if(function_exists("posix_kill")){ + posix_kill($pid, 9); //SIGKILL + }else{ + exec("kill -9 $pid > /dev/null 2>&1"); + } + } + } + + /** + * @param string $command Command to execute + * @param string|null $stdout Reference parameter to write stdout to + * @param string|null $stderr Reference parameter to write stderr to + * + * @return int process exit code + */ + public static function execute(string $command, string &$stdout = null, string &$stderr = null) : int{ + $process = proc_open($command, [ + ["pipe", "r"], + ["pipe", "w"], + ["pipe", "w"] + ], $pipes); + + if($process === false){ + $stderr = "Failed to open process"; + $stdout = ""; + + return -1; + } + + $stdout = stream_get_contents($pipes[1]); + $stderr = stream_get_contents($pipes[2]); + + foreach($pipes as $p){ + fclose($p); + } + + return proc_close($process); + } +} diff --git a/src/pocketmine/utils/ServerKiller.php b/src/pocketmine/utils/ServerKiller.php index da43895f9..67d0562bc 100644 --- a/src/pocketmine/utils/ServerKiller.php +++ b/src/pocketmine/utils/ServerKiller.php @@ -55,7 +55,7 @@ class ServerKiller extends Thread{ }); if(time() - $start >= $this->time){ echo "\nTook too long to stop, server was killed forcefully!\n"; - @Utils::kill(getmypid()); + @Process::kill(getmypid()); } } diff --git a/src/pocketmine/utils/Terminal.php b/src/pocketmine/utils/Terminal.php index ec71de7b3..9485acc9b 100644 --- a/src/pocketmine/utils/Terminal.php +++ b/src/pocketmine/utils/Terminal.php @@ -91,6 +91,7 @@ abstract class Terminal{ private static function detectFormattingCodesSupport() : bool{ $stdout = fopen("php://stdout", "w"); + if($stdout === false) throw new AssumptionFailedError("Opening php://stdout should never fail"); $result = ( stream_isatty($stdout) and //STDOUT isn't being piped ( @@ -181,14 +182,14 @@ abstract class Terminal{ } switch(Utils::getOS()){ - case "linux": - case "mac": - case "bsd": + case Utils::OS_LINUX: + case Utils::OS_MACOS: + case Utils::OS_BSD: self::getEscapeCodes(); return; - case "win": - case "android": + case Utils::OS_WINDOWS: + case Utils::OS_ANDROID: self::getFallbackEscapeCodes(); return; } @@ -290,4 +291,19 @@ abstract class Terminal{ return $newString; } + + /** + * Emits a string containing Minecraft colour codes to the console formatted with native colours. + */ + public static function write(string $line) : void{ + echo self::toANSI($line); + } + + /** + * Emits a string containing Minecraft colour codes to the console formatted with native colours, followed by a + * newline character. + */ + public static function writeLine(string $line) : void{ + echo self::toANSI($line) . self::$FORMAT_RESET . PHP_EOL; + } } diff --git a/src/pocketmine/utils/TextFormat.php b/src/pocketmine/utils/TextFormat.php index d02973c9b..6d106db1c 100644 --- a/src/pocketmine/utils/TextFormat.php +++ b/src/pocketmine/utils/TextFormat.php @@ -66,13 +66,19 @@ abstract class TextFormat{ public const ITALIC = TextFormat::ESCAPE . "o"; public const RESET = TextFormat::ESCAPE . "r"; + private static function makePcreError(string $info) : \InvalidArgumentException{ + throw new \InvalidArgumentException("$info: Encountered PCRE error " . preg_last_error() . " during regex operation"); + } + /** * Splits the string by Format tokens * * @return string[] */ public static function tokenize(string $string) : array{ - return preg_split("/(" . TextFormat::ESCAPE . "[0-9a-fk-or])/u", $string, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); + $result = preg_split("/(" . TextFormat::ESCAPE . "[0-9a-fk-or])/u", $string, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); + if($result === false) throw self::makePcreError("Failed to tokenize string"); + return $result; } /** @@ -83,6 +89,7 @@ abstract class TextFormat{ public static function clean(string $string, bool $removeFormat = true) : string{ $string = mb_scrub($string, 'UTF-8'); $string = preg_replace("/[\x{E000}-\x{F8FF}]/u", "", $string); //remove unicode private-use-area characters (they might break the console) + if($string === null) throw self::makePcreError("Failed to strip private-area characters"); if($removeFormat){ $string = str_replace(TextFormat::ESCAPE, "", preg_replace("/" . TextFormat::ESCAPE . "[0-9a-fk-or]/u", "", $string)); } @@ -281,7 +288,11 @@ abstract class TextFormat{ } } - return json_encode($newString, JSON_UNESCAPED_SLASHES); + $result = json_encode($newString, JSON_UNESCAPED_SLASHES); + if($result === false){ + throw new \InvalidArgumentException("Failed to encode result JSON: " . json_last_error_msg()); + } + return $result; } /** diff --git a/src/pocketmine/utils/Timezone.php b/src/pocketmine/utils/Timezone.php index 11d260925..4c7e29c1d 100644 --- a/src/pocketmine/utils/Timezone.php +++ b/src/pocketmine/utils/Timezone.php @@ -48,7 +48,11 @@ use function trim; abstract class Timezone{ public static function get() : string{ - return ini_get('date.timezone'); + $tz = ini_get('date.timezone'); + if($tz === false){ + throw new AssumptionFailedError('date.timezone INI entry should always exist'); + } + return $tz; } /** @@ -57,7 +61,7 @@ abstract class Timezone{ public static function init() : array{ $messages = []; do{ - $timezone = ini_get("date.timezone"); + $timezone = self::get(); if($timezone !== ""){ /* * This is here so that people don't come to us complaining and fill up the issue tracker when they put @@ -109,7 +113,7 @@ abstract class Timezone{ */ public static function detectSystemTimezone(){ switch(Utils::getOS()){ - case 'win': + case Utils::OS_WINDOWS: $regex = '/(UTC)(\+*\-*\d*\d*\:*\d*\d*)/'; /* @@ -144,21 +148,17 @@ abstract class Timezone{ } return self::parseOffset($offset); - case 'linux': + case Utils::OS_LINUX: // Ubuntu / Debian. - if(file_exists('/etc/timezone')){ - $data = file_get_contents('/etc/timezone'); - if($data){ - return trim($data); - } + $data = @file_get_contents('/etc/timezone'); + if($data !== false){ + return trim($data); } // RHEL / CentOS - if(file_exists('/etc/sysconfig/clock')){ - $data = parse_ini_file('/etc/sysconfig/clock'); - if(isset($data['ZONE']) and is_string($data['ZONE'])){ - return trim($data['ZONE']); - } + $data = @parse_ini_file('/etc/sysconfig/clock'); + if($data !== false and isset($data['ZONE']) and is_string($data['ZONE'])){ + return trim($data['ZONE']); } //Portable method for incompatible linux distributions. @@ -170,13 +170,11 @@ abstract class Timezone{ } return self::parseOffset($offset); - case 'mac': - if(is_link('/etc/localtime')){ - $filename = readlink('/etc/localtime'); - if(strpos($filename, '/usr/share/zoneinfo/') === 0){ - $timezone = substr($filename, 20); - return trim($timezone); - } + case Utils::OS_MACOS: + $filename = @readlink('/etc/localtime'); + if($filename !== false and strpos($filename, '/usr/share/zoneinfo/') === 0){ + $timezone = substr($filename, 20); + return trim($timezone); } return false; @@ -188,7 +186,7 @@ abstract class Timezone{ /** * @param string $offset In the format of +09:00, +02:00, -04:00 etc. * - * @return string|bool + * @return string|false */ private static function parseOffset($offset){ //Make signed offsets unsigned for date_parse diff --git a/src/pocketmine/utils/UUID.php b/src/pocketmine/utils/UUID.php index d5792bc0d..aa771f2e0 100644 --- a/src/pocketmine/utils/UUID.php +++ b/src/pocketmine/utils/UUID.php @@ -61,7 +61,12 @@ class UUID{ * Creates an UUID from an hexadecimal representation */ public static function fromString(string $uuid, int $version = null) : UUID{ - return self::fromBinary(hex2bin(str_replace("-", "", trim($uuid))), $version); + //TODO: should we be stricter about the notation (8-4-4-4-12)? + $binary = @hex2bin(str_replace("-", "", trim($uuid))); + if($binary === false){ + throw new \InvalidArgumentException("Invalid hex string UUID representation"); + } + return self::fromBinary($binary, $version); } /** diff --git a/src/pocketmine/utils/Utils.php b/src/pocketmine/utils/Utils.php index a6bb9c2d9..0a37267c5 100644 --- a/src/pocketmine/utils/Utils.php +++ b/src/pocketmine/utils/Utils.php @@ -28,7 +28,6 @@ declare(strict_types=1); namespace pocketmine\utils; use DaveRandom\CallbackValidator\CallbackType; -use pocketmine\ThreadManager; use function array_combine; use function array_map; use function array_reverse; @@ -42,7 +41,6 @@ use function dechex; use function error_reporting; use function exec; use function explode; -use function fclose; use function file; use function file_exists; use function file_get_contents; @@ -51,35 +49,33 @@ use function get_current_user; use function get_loaded_extensions; use function getenv; use function gettype; -use function hexdec; use function implode; use function is_array; +use function is_dir; +use function is_file; use function is_object; use function is_readable; use function is_string; use function json_decode; use function ltrim; -use function memory_get_usage; use function ob_end_clean; use function ob_get_contents; use function ob_start; use function ord; use function php_uname; use function phpversion; -use function posix_kill; use function preg_grep; use function preg_match; use function preg_match_all; use function preg_replace; -use function proc_close; -use function proc_open; +use function rmdir; use function rtrim; +use function scandir; use function sha1; use function spl_object_hash; use function str_pad; use function str_replace; use function str_split; -use function stream_get_contents; use function stripos; use function strlen; use function strpos; @@ -88,12 +84,14 @@ use function strtr; use function substr; use function sys_get_temp_dir; use function trim; +use function unlink; use function xdebug_get_function_stack; use const DIRECTORY_SEPARATOR; use const PHP_EOL; use const PHP_INT_MAX; use const PHP_INT_SIZE; use const PHP_MAXPATHLEN; +use const SCANDIR_SORT_NONE; use const STR_PAD_LEFT; use const STR_PAD_RIGHT; @@ -101,6 +99,14 @@ use const STR_PAD_RIGHT; * Big collection of functions */ class Utils{ + public const OS_WINDOWS = "win"; + public const OS_IOS = "ios"; + public const OS_MACOS = "mac"; + public const OS_ANDROID = "android"; + public const OS_LINUX = "linux"; + public const OS_BSD = "bsd"; + public const OS_UNKNOWN = "other"; + /** @var string|null */ public static $os; /** @var UUID|null */ @@ -114,8 +120,10 @@ class Utils{ public static function getCallableIdentifier(callable $variable){ if(is_array($variable)){ return sha1(strtolower(spl_object_hash($variable[0])) . "::" . strtolower($variable[1])); - }else{ + }elseif(is_string($variable)){ return sha1(strtolower($variable)); + }else{ + throw new AssumptionFailedError("Unhandled callable type"); } } @@ -141,7 +149,12 @@ class Utils{ //non-class function return $func->getName(); } - return "closure@" . self::cleanPath($func->getFileName()) . "#L" . $func->getStartLine(); + $filename = $func->getFileName(); + + return "closure@" . ($filename !== false ? + self::cleanPath($filename) . "#L" . $func->getStartLine() : + "internal" + ); } /** @@ -152,7 +165,12 @@ class Utils{ public static function getNiceClassName(object $obj) : string{ $reflect = new \ReflectionClass($obj); if($reflect->isAnonymous()){ - return "anonymous@" . self::cleanPath($reflect->getFileName()) . "#L" . $reflect->getStartLine(); + $filename = $reflect->getFileName(); + + return "anonymous@" . ($filename !== false ? + self::cleanPath($filename) . "#L" . $reflect->getStartLine() : + "internal" + ); } return $reflect->getName(); @@ -172,14 +190,14 @@ class Utils{ } $machine = php_uname("a"); - $machine .= file_exists("/proc/cpuinfo") ? implode(preg_grep("/(model name|Processor|Serial)/", file("/proc/cpuinfo"))) : ""; + $machine .= ($cpuinfo = @file("/proc/cpuinfo")) !== false ? implode(preg_grep("/(model name|Processor|Serial)/", $cpuinfo)) : ""; $machine .= sys_get_temp_dir(); $machine .= $extra; $os = Utils::getOS(); - if($os === "win"){ + if($os === Utils::OS_WINDOWS){ @exec("ipconfig /ALL", $mac); $mac = implode("\n", $mac); - if(preg_match_all("#Physical Address[. ]{1,}: ([0-9A-F\\-]{17})#", $mac, $matches)){ + if(preg_match_all("#Physical Address[. ]{1,}: ([0-9A-F\\-]{17})#", $mac, $matches) > 0){ foreach($matches[1] as $i => $v){ if($v == "00-00-00-00-00-00"){ unset($matches[1][$i]); @@ -187,13 +205,13 @@ class Utils{ } $machine .= implode(" ", $matches[1]); //Mac Addresses } - }elseif($os === "linux"){ + }elseif($os === Utils::OS_LINUX){ if(file_exists("/etc/machine-id")){ $machine .= file_get_contents("/etc/machine-id"); }else{ @exec("ifconfig 2>/dev/null", $mac); $mac = implode("\n", $mac); - if(preg_match_all("#HWaddr[ \t]{1,}([0-9a-f:]{17})#", $mac, $matches)){ + if(preg_match_all("#HWaddr[ \t]{1,}([0-9a-f:]{17})#", $mac, $matches) > 0){ foreach($matches[1] as $i => $v){ if($v == "00:00:00:00:00:00"){ unset($matches[1][$i]); @@ -202,9 +220,9 @@ class Utils{ $machine .= implode(" ", $matches[1]); //Mac Addresses } } - }elseif($os === "android"){ + }elseif($os === Utils::OS_ANDROID){ $machine .= @file_get_contents("/system/build.prop"); - }elseif($os === "mac"){ + }elseif($os === Utils::OS_MACOS){ $machine .= `system_profiler SPHardwareDataType | grep UUID`; } $data = $machine . PHP_MAXPATHLEN; @@ -251,22 +269,22 @@ class Utils{ $uname = php_uname("s"); if(stripos($uname, "Darwin") !== false){ if(strpos(php_uname("m"), "iP") === 0){ - self::$os = "ios"; + self::$os = self::OS_IOS; }else{ - self::$os = "mac"; + self::$os = self::OS_MACOS; } }elseif(stripos($uname, "Win") !== false or $uname === "Msys"){ - self::$os = "win"; + self::$os = self::OS_WINDOWS; }elseif(stripos($uname, "Linux") !== false){ if(@file_exists("/system/build.prop")){ - self::$os = "android"; + self::$os = self::OS_ANDROID; }else{ - self::$os = "linux"; + self::$os = self::OS_LINUX; } }elseif(stripos($uname, "BSD") !== false or $uname === "DragonFly"){ - self::$os = "bsd"; + self::$os = self::OS_BSD; }else{ - self::$os = "other"; + self::$os = self::OS_UNKNOWN; } } @@ -274,72 +292,32 @@ class Utils{ } /** + * @deprecated + * @see Process::getRealMemoryUsage() + * * @return int[] */ public static function getRealMemoryUsage() : array{ - $stack = 0; - $heap = 0; - - if(Utils::getOS() === "linux" or Utils::getOS() === "android"){ - $mappings = file("/proc/self/maps"); - foreach($mappings as $line){ - if(preg_match("#([a-z0-9]+)\\-([a-z0-9]+) [rwxp\\-]{4} [a-z0-9]+ [^\\[]*\\[([a-zA-z0-9]+)\\]#", trim($line), $matches) > 0){ - if(strpos($matches[3], "heap") === 0){ - $heap += hexdec($matches[2]) - hexdec($matches[1]); - }elseif(strpos($matches[3], "stack") === 0){ - $stack += hexdec($matches[2]) - hexdec($matches[1]); - } - } - } - } - - return [$heap, $stack]; + return Process::getRealMemoryUsage(); } /** + * @deprecated + * @see Process::getMemoryUsage() + * @see Process::getAdvancedMemoryUsage() + * * @return int[]|int */ public static function getMemoryUsage(bool $advanced = false){ - $reserved = memory_get_usage(); - $VmSize = null; - $VmRSS = null; - if(Utils::getOS() === "linux" or Utils::getOS() === "android"){ - $status = file_get_contents("/proc/self/status"); - if(preg_match("/VmRSS:[ \t]+([0-9]+) kB/", $status, $matches) > 0){ - $VmRSS = $matches[1] * 1024; - } - - if(preg_match("/VmSize:[ \t]+([0-9]+) kB/", $status, $matches) > 0){ - $VmSize = $matches[1] * 1024; - } - } - - //TODO: more OS - - if($VmRSS === null){ - $VmRSS = memory_get_usage(); - } - - if(!$advanced){ - return $VmRSS; - } - - if($VmSize === null){ - $VmSize = memory_get_usage(true); - } - - return [$reserved, $VmRSS, $VmSize]; + return $advanced ? Process::getAdvancedMemoryUsage() : Process::getMemoryUsage(); } + /** + * @deprecated + * @see Process::getThreadCount() + */ public static function getThreadCount() : int{ - if(Utils::getOS() === "linux" or Utils::getOS() === "android"){ - if(preg_match("/Threads:[ \t]+([0-9]+)/", file_get_contents("/proc/self/status"), $matches) > 0){ - return (int) $matches[1]; - } - } - //TODO: more OS - - return count(ThreadManager::getInstance()->getAll()) + 3; //RakLib + MainLogger + Main Thread + return Process::getThreadCount(); } public static function getCoreCount(bool $recalculate = false) : int{ @@ -352,25 +330,25 @@ class Utils{ } switch(Utils::getOS()){ - case "linux": - case "android": - if(file_exists("/proc/cpuinfo")){ - foreach(file("/proc/cpuinfo") as $l){ + case Utils::OS_LINUX: + case Utils::OS_ANDROID: + if(($cpuinfo = @file('/proc/cpuinfo')) !== false){ + foreach($cpuinfo as $l){ if(preg_match('/^processor[ \t]*:[ \t]*[0-9]+$/m', $l) > 0){ ++$processors; } } - }elseif(is_readable("/sys/devices/system/cpu/present")){ - if(preg_match("/^([0-9]+)\\-([0-9]+)$/", trim(file_get_contents("/sys/devices/system/cpu/present")), $matches) > 0){ + }elseif(($cpuPresent = @file_get_contents("/sys/devices/system/cpu/present")) !== false){ + if(preg_match("/^([0-9]+)\\-([0-9]+)$/", trim($cpuPresent), $matches) > 0){ $processors = (int) ($matches[2] - $matches[1]); } } break; - case "bsd": - case "mac": + case Utils::OS_BSD: + case Utils::OS_MACOS: $processors = (int) `sysctl -n hw.ncpu`; break; - case "win": + case Utils::OS_WINDOWS: $processors = (int) getenv("NUMBER_OF_PROCESSORS"); break; } @@ -495,6 +473,9 @@ class Utils{ } /** + * @deprecated + * @see Process::execute() + * * @param string $command Command to execute * @param string|null $stdout Reference parameter to write stdout to * @param string|null $stderr Reference parameter to write stderr to @@ -502,27 +483,7 @@ class Utils{ * @return int process exit code */ public static function execute(string $command, string &$stdout = null, string &$stderr = null) : int{ - $process = proc_open($command, [ - ["pipe", "r"], - ["pipe", "w"], - ["pipe", "w"] - ], $pipes); - - if($process === false){ - $stderr = "Failed to open process"; - $stdout = ""; - - return -1; - } - - $stdout = stream_get_contents($pipes[1]); - $stderr = stream_get_contents($pipes[2]); - - foreach($pipes as $p){ - fclose($p); - } - - return proc_close($process); + return Process::execute($command, $stdout, $stderr); } /** @@ -530,31 +491,28 @@ class Utils{ * @phpstan-return array */ public static function decodeJWT(string $token) : array{ - list($headB64, $payloadB64, $sigB64) = explode(".", $token); + [$headB64, $payloadB64, $sigB64] = explode(".", $token); - return json_decode(base64_decode(strtr($payloadB64, '-_', '+/'), true), true); + $rawPayloadJSON = base64_decode(strtr($payloadB64, '-_', '+/'), true); + if($rawPayloadJSON === false){ + throw new \InvalidArgumentException("Payload base64 is invalid and cannot be decoded"); + } + $decodedPayload = json_decode($rawPayloadJSON, true); + if(!is_array($decodedPayload)){ + throw new \InvalidArgumentException("Decoded payload should be array, " . gettype($decodedPayload) . " received"); + } + + return $decodedPayload; } /** + * @deprecated + * @see Process::kill() + * * @param int $pid */ public static function kill($pid) : void{ - if(MainLogger::isRegisteredStatic()){ - MainLogger::getLogger()->syncFlushBuffer(); - } - switch(Utils::getOS()){ - case "win": - exec("taskkill.exe /F /PID $pid > NUL"); - break; - case "mac": - case "linux": - default: - if(function_exists("posix_kill")){ - posix_kill($pid, 9); //SIGKILL - }else{ - exec("kill -9 $pid > /dev/null 2>&1"); - } - } + Process::kill($pid); } /** @@ -565,7 +523,9 @@ class Utils{ public static function getReferenceCount($value, bool $includeCurrent = true){ ob_start(); debug_zval_dump($value); - $ret = explode("\n", ob_get_contents()); + $contents = ob_get_contents(); + if($contents === false) throw new AssumptionFailedError("ob_get_contents() should never return false here"); + $ret = explode("\n", $contents); ob_end_clean(); if(count($ret) >= 1 and preg_match('/^.* refcount\\(([0-9]+)\\)\\{$/', trim($ret[0]), $m) > 0){ @@ -669,7 +629,9 @@ class Utils{ } preg_match_all('/(*ANYCRLF)^[\t ]*(?:\* )?@([a-zA-Z]+)(?:[\t ]+(.+?))?[\t ]*$/m', $rawDocComment, $matches); - return array_combine($matches[1], $matches[2]); + $result = array_combine($matches[1], $matches[2]); + if($result === false) throw new AssumptionFailedError("array_combine() doesn't return false with two equal-sized arrays"); + return $result; } /** @@ -683,6 +645,10 @@ class Utils{ return true; //stfu operator } + /** + * @phpstan-param class-string $className + * @phpstan-param class-string $baseName + */ public static function testValidInstance(string $className, string $baseName) : void{ try{ $base = new \ReflectionClass($baseName); @@ -719,4 +685,23 @@ class Utils{ throw new \TypeError("Declaration of callable `" . CallbackType::createFromCallable($subject) . "` must be compatible with `" . $sigType . "`"); } } + + public static function recursiveUnlink(string $dir) : void{ + if(is_dir($dir)){ + $objects = scandir($dir, SCANDIR_SORT_NONE); + if($objects === false) throw new AssumptionFailedError("scandir() shouldn't return false when is_dir() returns true"); + foreach($objects as $object){ + if($object !== "." and $object !== ".."){ + if(is_dir($dir . "/" . $object)){ + self::recursiveUnlink($dir . "/" . $object); + }else{ + unlink($dir . "/" . $object); + } + } + } + rmdir($dir); + }elseif(is_file($dir)){ + unlink($dir); + } + } } diff --git a/tests/phpstan/configs/l7-baseline.neon b/tests/phpstan/configs/l7-baseline.neon index 49c4d1b71..d09af1cf3 100644 --- a/tests/phpstan/configs/l7-baseline.neon +++ b/tests/phpstan/configs/l7-baseline.neon @@ -15,61 +15,6 @@ parameters: count: 1 path: ../../../build/server-phar.php - - - message: "#^Property pocketmine\\\\CrashDump\\:\\:\\$fp \\(resource\\) does not accept resource\\|false\\.$#" - count: 1 - path: ../../../src/pocketmine/CrashDump.php - - - - message: "#^Offset 'file' does not exist on array\\(\\?'type' \\=\\> int, \\?'message' \\=\\> string, \\?'file' \\=\\> string, \\?'line' \\=\\> int, 'trace' \\=\\> array\\\\>\\)\\.$#" - count: 1 - path: ../../../src/pocketmine/CrashDump.php - - - - message: "#^Offset 'file' does not exist on array\\(\\?'type' \\=\\> int, \\?'message' \\=\\> string, \\?'file' \\=\\> string, \\?'line' \\=\\> int, 'trace' \\=\\> array\\\\>, 'fullFile' \\=\\> string\\)\\.$#" - count: 1 - path: ../../../src/pocketmine/CrashDump.php - - - - message: "#^Offset 'type' does not exist on array\\(\\?'type' \\=\\> int, \\?'message' \\=\\> string, 'file' \\=\\> string, \\?'line' \\=\\> int, 'trace' \\=\\> array\\\\>, 'fullFile' \\=\\> string\\)\\.$#" - count: 2 - path: ../../../src/pocketmine/CrashDump.php - - - - message: "#^Offset 'message' does not exist on array\\('type' \\=\\> int\\|string, \\?'message' \\=\\> string, 'file' \\=\\> string, \\?'line' \\=\\> int, 'trace' \\=\\> array\\\\>, 'fullFile' \\=\\> string\\)\\.$#" - count: 2 - path: ../../../src/pocketmine/CrashDump.php - - - - message: "#^Cannot access offset float\\|int on array\\\\|false\\.$#" - count: 1 - path: ../../../src/pocketmine/CrashDump.php - - - - message: "#^Cannot access offset 0 on array\\\\|int\\.$#" - count: 1 - path: ../../../src/pocketmine/MemoryManager.php - - - - message: "#^Cannot access offset 1 on array\\\\|int\\.$#" - count: 1 - path: ../../../src/pocketmine/MemoryManager.php - - - - message: "#^Cannot access offset 0\\|1 on array\\\\|int\\.$#" - count: 2 - path: ../../../src/pocketmine/MemoryManager.php - - - - message: "#^Only booleans are allowed in an if condition, ReflectionClass\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/MemoryManager.php - - - - message: "#^Cannot call method getName\\(\\) on ReflectionClass\\|false\\.$#" - count: 1 - path: ../../../src/pocketmine/MemoryManager.php - - message: "#^Parameter \\#1 \\$fp of function fwrite expects resource, resource\\|false given\\.$#" count: 1 @@ -80,11 +25,6 @@ parameters: count: 1 path: ../../../src/pocketmine/MemoryManager.php - - - message: "#^Parameter \\#2 \\$newvalue of function ini_set expects string, string\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/MemoryManager.php - - message: "#^Parameter \\#3 \\$data of class pocketmine\\\\network\\\\mcpe\\\\protocol\\\\types\\\\SkinImage constructor expects string, string\\|false given\\.$#" count: 3 @@ -205,21 +145,6 @@ parameters: count: 1 path: ../../../src/pocketmine/Server.php - - - message: "#^Cannot access offset 0 on array\\\\|int\\.$#" - count: 1 - path: ../../../src/pocketmine/Server.php - - - - message: "#^Cannot access offset 1 on array\\\\|int\\.$#" - count: 1 - path: ../../../src/pocketmine/Server.php - - - - message: "#^Cannot access offset 2 on array\\\\|int\\.$#" - count: 1 - path: ../../../src/pocketmine/Server.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\level\\\\Level\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" count: 1 @@ -455,71 +380,11 @@ parameters: count: 1 path: ../../../src/pocketmine/command/defaults/BanIpCommand.php - - - message: "#^Cannot call method getMaxStackSize\\(\\) on array\\\\|pocketmine\\\\item\\\\Item\\.$#" - count: 1 - path: ../../../src/pocketmine/command/defaults/GiveCommand.php - - - - message: "#^Cannot call method setCount\\(\\) on array\\\\|pocketmine\\\\item\\\\Item\\.$#" - count: 2 - path: ../../../src/pocketmine/command/defaults/GiveCommand.php - - - - message: "#^Cannot call method setNamedTag\\(\\) on array\\\\|pocketmine\\\\item\\\\Item\\.$#" - count: 1 - path: ../../../src/pocketmine/command/defaults/GiveCommand.php - - - - message: "#^Cannot clone non\\-object variable \\$item of type array\\\\|pocketmine\\\\item\\\\Item\\.$#" - count: 1 - path: ../../../src/pocketmine/command/defaults/GiveCommand.php - - - - message: "#^Parameter \\#1 \\.\\.\\.\\$slots of method pocketmine\\\\inventory\\\\BaseInventory\\:\\:addItem\\(\\) expects pocketmine\\\\item\\\\Item, array\\\\|pocketmine\\\\item\\\\Item given\\.$#" - count: 1 - path: ../../../src/pocketmine/command/defaults/GiveCommand.php - - - - message: "#^Cannot call method getDamage\\(\\) on array\\\\|pocketmine\\\\item\\\\Item\\.$#" - count: 1 - path: ../../../src/pocketmine/command/defaults/GiveCommand.php - - - - message: "#^Cannot call method getId\\(\\) on array\\\\|pocketmine\\\\item\\\\Item\\.$#" - count: 1 - path: ../../../src/pocketmine/command/defaults/GiveCommand.php - - - - message: "#^Cannot call method getName\\(\\) on array\\\\|pocketmine\\\\item\\\\Item\\.$#" - count: 1 - path: ../../../src/pocketmine/command/defaults/GiveCommand.php - - - - message: "#^Cannot call method getCount\\(\\) on array\\\\|pocketmine\\\\item\\\\Item\\.$#" - count: 1 - path: ../../../src/pocketmine/command/defaults/GiveCommand.php - - message: "#^Only booleans are allowed in an if condition, int\\|false given\\.$#" count: 1 path: ../../../src/pocketmine/command/defaults/PardonIpCommand.php - - - message: "#^Cannot access offset 0 on array\\\\|int\\.$#" - count: 1 - path: ../../../src/pocketmine/command/defaults/StatusCommand.php - - - - message: "#^Cannot access offset 1 on array\\\\|int\\.$#" - count: 1 - path: ../../../src/pocketmine/command/defaults/StatusCommand.php - - - - message: "#^Cannot access offset 2 on array\\\\|int\\.$#" - count: 2 - path: ../../../src/pocketmine/command/defaults/StatusCommand.php - - message: "#^Parameter \\#1 \\$fp of static method pocketmine\\\\timings\\\\TimingsHandler\\:\\:printTimings\\(\\) expects resource, resource\\|false given\\.$#" count: 1 @@ -605,16 +470,6 @@ parameters: count: 1 path: ../../../src/pocketmine/event/HandlerList.php - - - message: "#^Cannot access offset 0 on array\\\\|int\\.$#" - count: 1 - path: ../../../src/pocketmine/event/server/LowMemoryEvent.php - - - - message: "#^Cannot access offset 1 on array\\\\|int\\.$#" - count: 1 - path: ../../../src/pocketmine/event/server/LowMemoryEvent.php - - message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#" count: 1 @@ -635,31 +490,6 @@ parameters: count: 1 path: ../../../src/pocketmine/item/Item.php - - - message: "#^Cannot call method setDamage\\(\\) on array\\\\|pocketmine\\\\item\\\\Item\\.$#" - count: 1 - path: ../../../src/pocketmine/item/Item.php - - - - message: "#^Cannot call method setCount\\(\\) on array\\\\|pocketmine\\\\item\\\\Item\\.$#" - count: 1 - path: ../../../src/pocketmine/item/Item.php - - - - message: "#^Cannot call method setNamedTag\\(\\) on array\\\\|pocketmine\\\\item\\\\Item\\.$#" - count: 1 - path: ../../../src/pocketmine/item/Item.php - - - - message: "#^Method pocketmine\\\\item\\\\Item\\:\\:nbtDeserialize\\(\\) should return pocketmine\\\\item\\\\Item but returns array\\\\|pocketmine\\\\item\\\\Item\\.$#" - count: 1 - path: ../../../src/pocketmine/item/Item.php - - - - message: "#^Method pocketmine\\\\item\\\\ItemFactory\\:\\:fromString\\(\\) should return array\\\\|pocketmine\\\\item\\\\Item but returns array\\\\|pocketmine\\\\item\\\\Item\\>\\.$#" - count: 1 - path: ../../../src/pocketmine/item/ItemFactory.php - - message: "#^Parameter \\#2 \\$input1 of function array_map expects array, array\\|false given\\.$#" count: 1 @@ -890,16 +720,6 @@ parameters: count: 1 path: ../../../src/pocketmine/level/Level.php - - - message: "#^Cannot call method getBlock\\(\\) on array\\\\|pocketmine\\\\item\\\\Item\\.$#" - count: 2 - path: ../../../src/pocketmine/level/Level.php - - - - message: "#^Cannot call method getId\\(\\) on array\\\\|pocketmine\\\\item\\\\Item\\.$#" - count: 2 - path: ../../../src/pocketmine/level/Level.php - - message: "#^Parameter \\#2 \\$amount of method pocketmine\\\\level\\\\Level\\:\\:dropExperience\\(\\) expects int, float\\|int\\<1, max\\> given\\.$#" count: 1 @@ -950,21 +770,6 @@ parameters: count: 1 path: ../../../src/pocketmine/level/format/io/region/RegionLoader.php - - - message: "#^Property pocketmine\\\\level\\\\format\\\\io\\\\region\\\\RegionLoader\\:\\:\\$filePointer \\(resource\\) does not accept resource\\|false\\.$#" - count: 1 - path: ../../../src/pocketmine/level/format/io/region/RegionLoader.php - - - - message: "#^Parameter \\#1 \\$fp of function stream_set_read_buffer expects resource, resource\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/level/format/io/region/RegionLoader.php - - - - message: "#^Parameter \\#1 \\$fp of function stream_set_write_buffer expects resource, resource\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/level/format/io/region/RegionLoader.php - - message: "#^Parameter \\#1 \\$string of function strlen expects string, string\\|false given\\.$#" count: 1 @@ -975,11 +780,6 @@ parameters: count: 1 path: ../../../src/pocketmine/level/format/io/region/RegionLoader.php - - - message: "#^Cannot call method getBlock\\(\\) on array\\\\|pocketmine\\\\item\\\\Item\\.$#" - count: 1 - path: ../../../src/pocketmine/level/generator/Flat.php - - message: "#^Parameter \\#1 \\$start of method pocketmine\\\\utils\\\\Random\\:\\:nextRange\\(\\) expects int, float\\|int given\\.$#" count: 2 @@ -1060,11 +860,6 @@ parameters: count: 1 path: ../../../src/pocketmine/network/mcpe/VerifyLoginTask.php - - - message: "#^Property pocketmine\\\\network\\\\mcpe\\\\protocol\\\\AddActorPacket\\:\\:\\$type \\(int\\) does not accept int\\|false\\.$#" - count: 1 - path: ../../../src/pocketmine/network/mcpe/protocol/AddActorPacket.php - - message: "#^Parameter \\#1 \\$str of method pocketmine\\\\utils\\\\BinaryStream\\:\\:put\\(\\) expects string, string\\|false given\\.$#" count: 1 @@ -1130,26 +925,6 @@ parameters: count: 1 path: ../../../src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php - - - message: "#^Property pocketmine\\\\network\\\\rcon\\\\RCON\\:\\:\\$socket \\(resource\\) does not accept resource\\|false\\.$#" - count: 1 - path: ../../../src/pocketmine/network/rcon/RCON.php - - - - message: "#^Parameter \\#1 \\$socket of function socket_set_option expects resource, resource\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/network/rcon/RCON.php - - - - message: "#^Only booleans are allowed in an if condition, bool\\|int given\\.$#" - count: 1 - path: ../../../src/pocketmine/network/rcon/RCONInstance.php - - - - message: "#^Only booleans are allowed in a negated boolean, bool\\|int given\\.$#" - count: 2 - path: ../../../src/pocketmine/network/rcon/RCONInstance.php - - message: "#^Call to an undefined method object\\:\\:Add\\(\\)\\.$#" count: 1 @@ -1160,56 +935,11 @@ parameters: count: 1 path: ../../../src/pocketmine/network/upnp/UPnP.php - - - message: "#^Method pocketmine\\\\plugin\\\\PluginBase\\:\\:getResource\\(\\) should return resource\\|null but returns resource\\|false\\.$#" - count: 1 - path: ../../../src/pocketmine/plugin/PluginBase.php - - - - message: "#^Parameter \\#2 \\$dest of function stream_copy_to_stream expects resource, resource\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/plugin/PluginBase.php - - - - message: "#^Parameter \\#1 \\$fp of function fclose expects resource, resource\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/plugin/PluginBase.php - - - - message: "#^Parameter \\#1 \\$version1 of function version_compare expects string, string\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/plugin/PluginDescription.php - - - - message: "#^Parameter \\#1 \\$path of method pocketmine\\\\plugin\\\\PluginLoader\\:\\:canLoadPlugin\\(\\) expects string, SplFileInfo\\|string given\\.$#" - count: 1 - path: ../../../src/pocketmine/plugin/PluginManager.php - - - - message: "#^Parameter \\#1 \\$file of method pocketmine\\\\plugin\\\\PluginLoader\\:\\:getPluginDescription\\(\\) expects string, SplFileInfo\\|string given\\.$#" - count: 1 - path: ../../../src/pocketmine/plugin/PluginManager.php - - - - message: "#^Parameter \\#2 \\$params of method pocketmine\\\\lang\\\\BaseLang\\:\\:translateString\\(\\) expects array\\, array\\ given\\.$#" - count: 1 - path: ../../../src/pocketmine/plugin/PluginManager.php - - - - message: "#^Parameter \\#1 \\$path of method pocketmine\\\\plugin\\\\PluginManager\\:\\:loadPlugin\\(\\) expects string, SplFileInfo\\|string given\\.$#" - count: 1 - path: ../../../src/pocketmine/plugin/PluginManager.php - - message: "#^Parameter \\#1 \\$event of method pocketmine\\\\plugin\\\\PluginManager\\:\\:registerEvent\\(\\) expects class\\-string\\, class\\-string\\ given\\.$#" count: 1 path: ../../../src/pocketmine/plugin/PluginManager.php - - - message: "#^Argument of an invalid type array\\\\|false supplied for foreach, only iterables are supported\\.$#" - count: 1 - path: ../../../src/pocketmine/plugin/ScriptPluginLoader.php - - message: "#^Parameter \\#1 \\$string of function strlen expects string, string\\|false given\\.$#" count: 2 @@ -1245,36 +975,6 @@ parameters: count: 1 path: ../../../src/pocketmine/resourcepacks/ZippedResourcePack.php - - - message: "#^Method pocketmine\\\\scheduler\\\\AsyncPool\\:\\:selectWorker\\(\\) should return int but returns int\\|string\\.$#" - count: 1 - path: ../../../src/pocketmine/scheduler/AsyncPool.php - - - - message: "#^Parameter \\#1 \\$variable_representation of function unserialize expects string, bool\\|float\\|int\\|string\\|null given\\.$#" - count: 1 - path: ../../../src/pocketmine/scheduler/AsyncTask.php - - - - message: "#^Cannot access offset 0 on array\\\\|int\\.$#" - count: 1 - path: ../../../src/pocketmine/scheduler/SendUsageTask.php - - - - message: "#^Cannot access offset 1 on array\\\\|int\\.$#" - count: 1 - path: ../../../src/pocketmine/scheduler/SendUsageTask.php - - - - message: "#^Cannot access offset 2 on array\\\\|int\\.$#" - count: 1 - path: ../../../src/pocketmine/scheduler/SendUsageTask.php - - - - message: "#^Property pocketmine\\\\scheduler\\\\SendUsageTask\\:\\:\\$data \\(string\\) does not accept string\\|false\\.$#" - count: 1 - path: ../../../src/pocketmine/scheduler/SendUsageTask.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\level\\\\Level\\:\\:getTileAt\\(\\) expects int, float\\|int given\\.$#" count: 1 @@ -1305,16 +1005,6 @@ parameters: count: 1 path: ../../../src/pocketmine/tile/Spawnable.php - - - message: "#^Property pocketmine\\\\tile\\\\Spawnable\\:\\:\\$spawnCompoundCache \\(string\\|null\\) does not accept string\\|false\\.$#" - count: 1 - path: ../../../src/pocketmine/tile/Spawnable.php - - - - message: "#^Method pocketmine\\\\tile\\\\Spawnable\\:\\:getSerializedSpawnCompound\\(\\) should return string but returns string\\|false\\.$#" - count: 1 - path: ../../../src/pocketmine/tile/Spawnable.php - - message: "#^Parameter \\#2 \\$value of class pocketmine\\\\nbt\\\\tag\\\\IntTag constructor expects int, float\\|int given\\.$#" count: 3 @@ -1340,243 +1030,3 @@ parameters: count: 1 path: ../../../src/pocketmine/tile/Tile.php - - - message: "#^Parameter \\#1 \\$content of method pocketmine\\\\utils\\\\Config\\:\\:parseProperties\\(\\) expects string, string\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Config.php - - - - message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Config.php - - - - message: "#^Parameter \\#1 \\$str of static method pocketmine\\\\utils\\\\Config\\:\\:fixYAMLIndexes\\(\\) expects string, string\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Config.php - - - - message: "#^Parameter \\#1 \\$variable_representation of function unserialize expects string, string\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Config.php - - - - message: "#^Parameter \\#1 \\$content of static method pocketmine\\\\utils\\\\Config\\:\\:parseList\\(\\) expects string, string\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Config.php - - - - message: "#^Method pocketmine\\\\utils\\\\Config\\:\\:parseProperties\\(\\) should return array\\ but returns array\\\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Config.php - - - - message: "#^Parameter \\#1 \\$socket of function socket_connect expects resource, resource\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Internet.php - - - - message: "#^Parameter \\#1 \\$socket of function socket_last_error expects resource, resource\\|false given\\.$#" - count: 2 - path: ../../../src/pocketmine/utils/Internet.php - - - - message: "#^Parameter \\#1 \\$socket of function socket_getsockname expects resource, resource\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Internet.php - - - - message: "#^Parameter \\#1 \\$socket of function socket_close expects resource, resource\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Internet.php - - - - message: "#^Parameter \\#1 \\$ch of function curl_setopt_array expects resource, resource\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Internet.php - - - - message: "#^Parameter \\#1 \\$ch of function curl_exec expects resource, resource\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Internet.php - - - - message: "#^Parameter \\#1 \\$ch of function curl_error expects resource, resource\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Internet.php - - - - message: "#^Parameter \\#1 \\$ch of function curl_getinfo expects resource, resource\\|false given\\.$#" - count: 2 - path: ../../../src/pocketmine/utils/Internet.php - - - - message: "#^Parameter \\#1 \\$str of function substr expects string, bool\\|string given\\.$#" - count: 2 - path: ../../../src/pocketmine/utils/Internet.php - - - - message: "#^Parameter \\#1 \\$ of callable callable\\(resource\\)\\: void expects resource, resource\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Internet.php - - - - message: "#^Parameter \\#1 \\$ch of function curl_close expects resource, resource\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Internet.php - - - - message: "#^Parameter \\#1 \\$stream of function stream_isatty expects resource, resource\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Terminal.php - - - - message: "#^Parameter \\#1 \\$stream of function sapi_windows_vt100_support expects resource, resource\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Terminal.php - - - - message: "#^Parameter \\#1 \\$fp of function fclose expects resource, resource\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Terminal.php - - - - message: "#^Method pocketmine\\\\utils\\\\TextFormat\\:\\:tokenize\\(\\) should return array\\ but returns array\\\\|false\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/TextFormat.php - - - - message: "#^Method pocketmine\\\\utils\\\\TextFormat\\:\\:toJSON\\(\\) should return string but returns string\\|false\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/TextFormat.php - - - - message: "#^Method pocketmine\\\\utils\\\\Timezone\\:\\:get\\(\\) should return string but returns string\\|false\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Timezone.php - - - - message: "#^Parameter \\#1 \\$haystack of function strpos expects string, string\\|false given\\.$#" - count: 2 - path: ../../../src/pocketmine/utils/Timezone.php - - - - message: "#^Parameter \\#1 \\$abbr of function timezone_name_from_abbr expects string, string\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Timezone.php - - - - message: "#^Parameter \\#1 \\$timezone_identifier of function date_default_timezone_set expects string, string\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Timezone.php - - - - message: "#^Method pocketmine\\\\utils\\\\Timezone\\:\\:detectSystemTimezone\\(\\) should return string\\|false but returns bool\\|string\\.$#" - count: 2 - path: ../../../src/pocketmine/utils/Timezone.php - - - - message: "#^Only booleans are allowed in an if condition, string\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Timezone.php - - - - message: "#^Cannot access offset 'ZONE' on array\\|false\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Timezone.php - - - - message: "#^Parameter \\#1 \\$str of function substr expects string, string\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Timezone.php - - - - message: "#^Parameter \\#1 \\$uuid of static method pocketmine\\\\utils\\\\UUID\\:\\:fromBinary\\(\\) expects string, string\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/UUID.php - - - - message: "#^Parameter \\#1 \\$str of function strtolower expects string, callable given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Utils.php - - - - message: "#^Parameter \\#1 \\$path of static method pocketmine\\\\utils\\\\Utils\\:\\:cleanPath\\(\\) expects string, string\\|false given\\.$#" - count: 2 - path: ../../../src/pocketmine/utils/Utils.php - - - - message: "#^Parameter \\#2 \\$input of function preg_grep expects array, array\\\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Utils.php - - - - message: "#^Only booleans are allowed in an if condition, int\\|false given\\.$#" - count: 2 - path: ../../../src/pocketmine/utils/Utils.php - - - - message: "#^Argument of an invalid type array\\\\|false supplied for foreach, only iterables are supported\\.$#" - count: 2 - path: ../../../src/pocketmine/utils/Utils.php - - - - message: "#^Method pocketmine\\\\utils\\\\Utils\\:\\:getRealMemoryUsage\\(\\) should return array\\ but returns array\\\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Utils.php - - - - message: "#^Parameter \\#2 \\$subject of function preg_match expects string, string\\|false given\\.$#" - count: 3 - path: ../../../src/pocketmine/utils/Utils.php - - - - message: "#^Method pocketmine\\\\utils\\\\Utils\\:\\:getMemoryUsage\\(\\) should return array\\\\|int but returns float\\|int\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Utils.php - - - - message: "#^Method pocketmine\\\\utils\\\\Utils\\:\\:getMemoryUsage\\(\\) should return array\\\\|int but returns array\\\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Utils.php - - - - message: "#^Parameter \\#1 \\$str of function trim expects string, string\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Utils.php - - - - message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Utils.php - - - - message: "#^Parameter \\#2 \\$str of function explode expects string, string\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Utils.php - - - - message: "#^Method pocketmine\\\\utils\\\\Utils\\:\\:parseDocComment\\(\\) should return array\\ but returns array\\|false\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/Utils.php - - - - message: "#^Parameter \\#1 \\$argument of class ReflectionClass constructor expects class\\-string\\\\|T of object, string given\\.$#" - count: 2 - path: ../../../src/pocketmine/utils/Utils.php - - - - message: "#^Parameter \\#2 \\$params of method pocketmine\\\\lang\\\\BaseLang\\:\\:translateString\\(\\) expects array\\, array\\ given\\.$#" - count: 1 - path: ../../../src/pocketmine/wizard/SetupWizard.php - - - - message: "#^Cannot call method getId\\(\\) on array\\\\|pocketmine\\\\item\\\\Item\\.$#" - count: 1 - path: ../../phpunit/item/ItemTest.php - - - - message: "#^Cannot call method getDamage\\(\\) on array\\\\|pocketmine\\\\item\\\\Item\\.$#" - count: 1 - path: ../../phpunit/item/ItemTest.php - diff --git a/tests/phpstan/configs/l8-baseline.neon b/tests/phpstan/configs/l8-baseline.neon index 210cd2a3f..d7c045156 100644 --- a/tests/phpstan/configs/l8-baseline.neon +++ b/tests/phpstan/configs/l8-baseline.neon @@ -107,7 +107,7 @@ parameters: - message: "#^Cannot call method getFolderName\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 3 + count: 2 path: ../../../src/pocketmine/Player.php - @@ -135,11 +135,6 @@ parameters: count: 1 path: ../../../src/pocketmine/Player.php - - - message: "#^Cannot call method broadcastPacketToViewers\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/Player.php - - message: "#^Cannot call method useItemOn\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 2 @@ -157,7 +152,7 @@ parameters: - message: "#^Cannot call method getTile\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 3 + count: 2 path: ../../../src/pocketmine/Player.php - @@ -196,7 +191,7 @@ parameters: path: ../../../src/pocketmine/Player.php - - message: "#^Cannot call method getLevel\\(\\) on pocketmine\\\\level\\\\Position\\|null\\.$#" + message: "#^Cannot call method getLevelNonNull\\(\\) on pocketmine\\\\level\\\\Position\\|null\\.$#" count: 1 path: ../../../src/pocketmine/Player.php @@ -280,61 +275,21 @@ parameters: count: 1 path: ../../../src/pocketmine/Worker.php - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Anvil.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 2 - path: ../../../src/pocketmine/block/BaseRail.php - - message: "#^Parameter \\#1 \\$constraint of method pocketmine\\\\block\\\\BaseRail\\:\\:getPossibleConnectionDirectionsOneConstraint\\(\\) expects int, int\\|null given\\.$#" count: 1 path: ../../../src/pocketmine/block/BaseRail.php - - message: "#^Cannot call method useBreakOn\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" + message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 1 path: ../../../src/pocketmine/block/BaseRail.php - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 3 - path: ../../../src/pocketmine/block/Bed.php - - - - message: "#^Cannot call method getTime\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Bed.php - - message: "#^Only numeric types are allowed in \\-, int\\|null given on the left side\\.$#" count: 1 path: ../../../src/pocketmine/block/Bed.php - - - message: "#^Parameter \\#2 \\$level of static method pocketmine\\\\tile\\\\Tile\\:\\:createTile\\(\\) expects pocketmine\\\\level\\\\Level, pocketmine\\\\level\\\\Level\\|null given\\.$#" - count: 2 - path: ../../../src/pocketmine/block/Bed.php - - - - message: "#^Cannot call method getTile\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Bed.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 2 - path: ../../../src/pocketmine/block/Block.php - - - - message: "#^Cannot call method getBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Block.php - - message: "#^Cannot call method getBlockMetadata\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 4 @@ -370,116 +325,21 @@ parameters: count: 1 path: ../../../src/pocketmine/block/BlockFactory.php - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/BoneBlock.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/BurningFurnace.php - - - - message: "#^Parameter \\#2 \\$level of static method pocketmine\\\\tile\\\\Tile\\:\\:createTile\\(\\) expects pocketmine\\\\level\\\\Level, pocketmine\\\\level\\\\Level\\|null given\\.$#" - count: 2 - path: ../../../src/pocketmine/block/BurningFurnace.php - - - - message: "#^Cannot call method getTile\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/BurningFurnace.php - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 1 path: ../../../src/pocketmine/block/Button.php - - message: "#^Cannot call method useBreakOn\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 2 - path: ../../../src/pocketmine/block/Cactus.php - - - - message: "#^Cannot call method getBlockAt\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" + message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 1 - path: ../../../src/pocketmine/block/Cactus.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 4 - path: ../../../src/pocketmine/block/Cactus.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 3 path: ../../../src/pocketmine/block/Cake.php - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Carpet.php - - - - message: "#^Cannot call method useBreakOn\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Carpet.php - - - - message: "#^Cannot call method getTile\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 2 - path: ../../../src/pocketmine/block/Chest.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Chest.php - - - - message: "#^Parameter \\#2 \\$level of static method pocketmine\\\\tile\\\\Tile\\:\\:createTile\\(\\) expects pocketmine\\\\level\\\\Level, pocketmine\\\\level\\\\Level\\|null given\\.$#" - count: 2 - path: ../../../src/pocketmine/block/Chest.php - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 1 path: ../../../src/pocketmine/block/ConcretePowder.php - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 3 - path: ../../../src/pocketmine/block/Crops.php - - - - message: "#^Cannot call method useBreakOn\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Crops.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Dandelion.php - - - - message: "#^Cannot call method useBreakOn\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Dandelion.php - - - - message: "#^Cannot call method useBreakOn\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/DeadBush.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 2 - path: ../../../src/pocketmine/block/Dirt.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 6 - path: ../../../src/pocketmine/block/Door.php - - message: "#^Only numeric types are allowed in \\+, int\\|null given on the left side\\.$#" count: 1 @@ -491,29 +351,14 @@ parameters: path: ../../../src/pocketmine/block/Door.php - - message: "#^Cannot call method addSound\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" + message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 2 path: ../../../src/pocketmine/block/Door.php - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" + message: "#^Cannot call method addSound\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 2 - path: ../../../src/pocketmine/block/DoublePlant.php - - - - message: "#^Cannot call method useBreakOn\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/DoublePlant.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/EnchantingTable.php - - - - message: "#^Parameter \\#2 \\$level of static method pocketmine\\\\tile\\\\Tile\\:\\:createTile\\(\\) expects pocketmine\\\\level\\\\Level, pocketmine\\\\level\\\\Level\\|null given\\.$#" - count: 1 - path: ../../../src/pocketmine/block/EnchantingTable.php + path: ../../../src/pocketmine/block/Door.php - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" @@ -523,26 +368,6 @@ parameters: - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 1 - path: ../../../src/pocketmine/block/EnderChest.php - - - - message: "#^Parameter \\#2 \\$level of static method pocketmine\\\\tile\\\\Tile\\:\\:createTile\\(\\) expects pocketmine\\\\level\\\\Level, pocketmine\\\\level\\\\Level\\|null given\\.$#" - count: 2 - path: ../../../src/pocketmine/block/EnderChest.php - - - - message: "#^Cannot call method getTile\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/EnderChest.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Fallable.php - - - - message: "#^Parameter \\#2 \\$level of static method pocketmine\\\\entity\\\\Entity\\:\\:createEntity\\(\\) expects pocketmine\\\\level\\\\Level, pocketmine\\\\level\\\\Level\\|null given\\.$#" - count: 1 path: ../../../src/pocketmine/block/Fallable.php - @@ -560,21 +385,11 @@ parameters: count: 2 path: ../../../src/pocketmine/block/FenceGate.php - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 2 - path: ../../../src/pocketmine/block/FenceGate.php - - message: "#^Cannot call method addSound\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 1 path: ../../../src/pocketmine/block/FenceGate.php - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 4 - path: ../../../src/pocketmine/block/Fire.php - - message: "#^Cannot call method scheduleDelayedBlockUpdate\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 2 @@ -582,49 +397,14 @@ parameters: - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Flower.php - - - - message: "#^Cannot call method useBreakOn\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Flower.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 2 - path: ../../../src/pocketmine/block/FlowerPot.php - - - - message: "#^Parameter \\#2 \\$level of static method pocketmine\\\\tile\\\\Tile\\:\\:createTile\\(\\) expects pocketmine\\\\level\\\\Level, pocketmine\\\\level\\\\Level\\|null given\\.$#" - count: 1 - path: ../../../src/pocketmine/block/FlowerPot.php - - - - message: "#^Cannot call method useBreakOn\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/FlowerPot.php - - - - message: "#^Cannot call method getTile\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 2 - path: ../../../src/pocketmine/block/FlowerPot.php + count: 3 + path: ../../../src/pocketmine/block/Fire.php - message: "#^Only numeric types are allowed in \\-, int\\|null given on the left side\\.$#" count: 1 path: ../../../src/pocketmine/block/GlazedTerracotta.php - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/GlazedTerracotta.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/GlowingRedstoneOre.php - - message: "#^Cannot call method getFullLightAt\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 2 @@ -637,7 +417,7 @@ parameters: - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 4 + count: 2 path: ../../../src/pocketmine/block/Grass.php - @@ -650,26 +430,11 @@ parameters: count: 1 path: ../../../src/pocketmine/block/Grass.php - - - message: "#^Parameter \\#1 \\$level of static method pocketmine\\\\level\\\\generator\\\\object\\\\TallGrass\\:\\:growGrass\\(\\) expects pocketmine\\\\level\\\\ChunkManager, pocketmine\\\\level\\\\Level\\|null given\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Grass.php - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 1 path: ../../../src/pocketmine/block/GrassPath.php - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/HayBale.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Ice.php - - message: "#^Cannot call method getHighestAdjacentBlockLight\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 1 @@ -685,11 +450,6 @@ parameters: count: 2 path: ../../../src/pocketmine/block/ItemFrame.php - - - message: "#^Parameter \\#2 \\$level of static method pocketmine\\\\tile\\\\Tile\\:\\:createTile\\(\\) expects pocketmine\\\\level\\\\Level, pocketmine\\\\level\\\\Level\\|null given\\.$#" - count: 2 - path: ../../../src/pocketmine/block/ItemFrame.php - - message: "#^Cannot call method useBreakOn\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 1 @@ -700,36 +460,11 @@ parameters: count: 1 path: ../../../src/pocketmine/block/ItemFrame.php - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Ladder.php - - message: "#^Cannot call method useBreakOn\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 1 path: ../../../src/pocketmine/block/Ladder.php - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Lava.php - - - - message: "#^Cannot call method scheduleDelayedBlockUpdate\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Lava.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 3 - path: ../../../src/pocketmine/block/Leaves.php - - - - message: "#^Cannot call method useBreakOn\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Leaves.php - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 1 @@ -765,245 +500,40 @@ parameters: count: 1 path: ../../../src/pocketmine/block/Liquid.php - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 2 - path: ../../../src/pocketmine/block/MelonStem.php - - - - message: "#^Cannot call method getBlockAt\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Mycelium.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Mycelium.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 2 - path: ../../../src/pocketmine/block/NetherWartPlant.php - - - - message: "#^Cannot call method useBreakOn\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/NetherWartPlant.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Pumpkin.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 2 - path: ../../../src/pocketmine/block/PumpkinStem.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Quartz.php - - - - message: "#^Cannot call method useBreakOn\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/RedMushroom.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/RedMushroom.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 3 - path: ../../../src/pocketmine/block/RedstoneOre.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 2 - path: ../../../src/pocketmine/block/Sapling.php - - - - message: "#^Parameter \\#1 \\$level of static method pocketmine\\\\level\\\\generator\\\\object\\\\Tree\\:\\:growTree\\(\\) expects pocketmine\\\\level\\\\ChunkManager, pocketmine\\\\level\\\\Level\\|null given\\.$#" - count: 2 - path: ../../../src/pocketmine/block/Sapling.php - - - - message: "#^Cannot call method useBreakOn\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Sapling.php - - message: "#^Cannot call method getFullLightAt\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 1 path: ../../../src/pocketmine/block/Sapling.php - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 2 - path: ../../../src/pocketmine/block/SignPost.php - - - - message: "#^Parameter \\#2 \\$level of static method pocketmine\\\\tile\\\\Tile\\:\\:createTile\\(\\) expects pocketmine\\\\level\\\\Level, pocketmine\\\\level\\\\Level\\|null given\\.$#" - count: 1 - path: ../../../src/pocketmine/block/SignPost.php - - - - message: "#^Cannot call method useBreakOn\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/SignPost.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Skull.php - - - - message: "#^Parameter \\#2 \\$level of static method pocketmine\\\\tile\\\\Tile\\:\\:createTile\\(\\) expects pocketmine\\\\level\\\\Level, pocketmine\\\\level\\\\Level\\|null given\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Skull.php - - message: "#^Cannot call method getTile\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 1 path: ../../../src/pocketmine/block/Skull.php - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 6 - path: ../../../src/pocketmine/block/Slab.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 3 - path: ../../../src/pocketmine/block/SnowLayer.php - - message: "#^Cannot call method getBlockLightAt\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 1 path: ../../../src/pocketmine/block/SnowLayer.php - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Stair.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 2 - path: ../../../src/pocketmine/block/StandingBanner.php - - - - message: "#^Parameter \\#2 \\$level of static method pocketmine\\\\tile\\\\Tile\\:\\:createTile\\(\\) expects pocketmine\\\\level\\\\Level, pocketmine\\\\level\\\\Level\\|null given\\.$#" - count: 1 - path: ../../../src/pocketmine/block/StandingBanner.php - - - - message: "#^Cannot call method useBreakOn\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/StandingBanner.php - - message: "#^Cannot call method getTile\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 1 path: ../../../src/pocketmine/block/StandingBanner.php - - - message: "#^Cannot call method getBlockAt\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 2 - path: ../../../src/pocketmine/block/Sugarcane.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 7 - path: ../../../src/pocketmine/block/Sugarcane.php - - - - message: "#^Cannot call method useBreakOn\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Sugarcane.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/TNT.php - - - - message: "#^Parameter \\#2 \\$level of static method pocketmine\\\\entity\\\\Entity\\:\\:createEntity\\(\\) expects pocketmine\\\\level\\\\Level, pocketmine\\\\level\\\\Level\\|null given\\.$#" - count: 1 - path: ../../../src/pocketmine/block/TNT.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 2 - path: ../../../src/pocketmine/block/TallGrass.php - - - - message: "#^Cannot call method useBreakOn\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Torch.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 2 - path: ../../../src/pocketmine/block/Torch.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 2 - path: ../../../src/pocketmine/block/Trapdoor.php - - message: "#^Cannot call method addSound\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 1 path: ../../../src/pocketmine/block/Trapdoor.php - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 2 - path: ../../../src/pocketmine/block/Vine.php - - message: "#^Cannot call method useBreakOn\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 1 path: ../../../src/pocketmine/block/Vine.php - - - message: "#^Cannot call method useBreakOn\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/WallBanner.php - - - - message: "#^Cannot call method useBreakOn\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/WallSign.php - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 1 - path: ../../../src/pocketmine/block/Water.php - - - - message: "#^Cannot call method scheduleDelayedBlockUpdate\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Water.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/WaterLily.php - - - - message: "#^Cannot call method useBreakOn\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/WaterLily.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/block/Wood.php + path: ../../../src/pocketmine/block/Vine.php - message: "#^Parameter \\#2 \\$replace of function str_replace expects array\\|string, string\\|null given\\.$#" @@ -1067,7 +597,7 @@ parameters: - message: "#^Cannot call method getSeed\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 2 + count: 1 path: ../../../src/pocketmine/command/defaults/SeedCommand.php - @@ -1075,11 +605,6 @@ parameters: count: 1 path: ../../../src/pocketmine/command/defaults/SetWorldSpawnCommand.php - - - message: "#^Cannot call method getSpawnLocation\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/command/defaults/SpawnpointCommand.php - - message: "#^Parameter \\#1 \\$name of method pocketmine\\\\Server\\:\\:getPlayer\\(\\) expects string, string\\|null given\\.$#" count: 1 @@ -1190,11 +715,6 @@ parameters: count: 1 path: ../../../src/pocketmine/entity/Entity.php - - - message: "#^Parameter \\#1 \\$targetLevel of method pocketmine\\\\entity\\\\Entity\\:\\:switchLevel\\(\\) expects pocketmine\\\\level\\\\Level, pocketmine\\\\level\\\\Level\\|null given\\.$#" - count: 1 - path: ../../../src/pocketmine/entity/Entity.php - - message: "#^Cannot call method getChunk\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 1 @@ -1320,16 +840,6 @@ parameters: count: 1 path: ../../../src/pocketmine/entity/Living.php - - - message: "#^Cannot call method getDifficulty\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/entity/Living.php - - - - message: "#^Cannot call method dropItem\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/entity/Living.php - - message: "#^Cannot call method dropExperience\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 1 @@ -1370,16 +880,6 @@ parameters: count: 1 path: ../../../src/pocketmine/entity/object/FallingBlock.php - - - message: "#^Cannot call method dropItem\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/entity/object/FallingBlock.php - - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/entity/object/FallingBlock.php - - message: "#^Cannot call method dropItem\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 1 @@ -1485,46 +985,21 @@ parameters: count: 2 path: ../../../src/pocketmine/entity/projectile/SplashPotion.php - - - message: "#^Cannot call method getServer\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/event/entity/EntityDamageByChildEntityEvent.php - - message: "#^Cannot call method getEffectLevel\\(\\) on pocketmine\\\\entity\\\\EffectInstance\\|null\\.$#" count: 2 path: ../../../src/pocketmine/event/entity/EntityDamageByEntityEvent.php - - - message: "#^Cannot call method getServer\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/event/entity/EntityDamageByEntityEvent.php - - message: "#^Property pocketmine\\\\inventory\\\\BaseInventory\\:\\:\\$eventProcessor \\(pocketmine\\\\inventory\\\\InventoryEventProcessor\\) does not accept pocketmine\\\\inventory\\\\InventoryEventProcessor\\|null\\.$#" count: 1 path: ../../../src/pocketmine/inventory/BaseInventory.php - - - message: "#^Cannot call method broadcastLevelSoundEvent\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 2 - path: ../../../src/pocketmine/inventory/ChestInventory.php - - - - message: "#^Cannot call method broadcastPacketToViewers\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/inventory/ChestInventory.php - - message: "#^Method pocketmine\\\\inventory\\\\CraftingManager\\:\\:getCraftingDataPacket\\(\\) should return pocketmine\\\\network\\\\mcpe\\\\protocol\\\\BatchPacket but returns pocketmine\\\\network\\\\mcpe\\\\protocol\\\\BatchPacket\\|null\\.$#" count: 1 path: ../../../src/pocketmine/inventory/CraftingManager.php - - - message: "#^Cannot call method getServer\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/inventory/PlayerInventory.php - - message: "#^Parameter \\#2 \\$recipe of class pocketmine\\\\event\\\\inventory\\\\CraftItemEvent constructor expects pocketmine\\\\inventory\\\\CraftingRecipe, pocketmine\\\\inventory\\\\CraftingRecipe\\|null given\\.$#" count: 1 @@ -1545,36 +1020,11 @@ parameters: count: 1 path: ../../../src/pocketmine/item/Banner.php - - - message: "#^Parameter \\#2 \\$level of static method pocketmine\\\\entity\\\\Entity\\:\\:createEntity\\(\\) expects pocketmine\\\\level\\\\Level, pocketmine\\\\level\\\\Level\\|null given\\.$#" - count: 1 - path: ../../../src/pocketmine/item/Bow.php - - - - message: "#^Cannot call method broadcastLevelSoundEvent\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/item/Bow.php - - message: "#^Cannot call method spawnToAll\\(\\) on pocketmine\\\\entity\\\\Entity\\|null\\.$#" count: 1 path: ../../../src/pocketmine/item/Bow.php - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 2 - path: ../../../src/pocketmine/item/Bucket.php - - - - message: "#^Cannot call method broadcastLevelSoundEvent\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 2 - path: ../../../src/pocketmine/item/Bucket.php - - - - message: "#^Cannot call method getWorldHeight\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/item/ChorusFruit.php - - message: "#^Parameter \\#1 \\$effectType of class pocketmine\\\\entity\\\\EffectInstance constructor expects pocketmine\\\\entity\\\\Effect, pocketmine\\\\entity\\\\Effect\\|null given\\.$#" count: 2 @@ -1595,16 +1045,6 @@ parameters: count: 1 path: ../../../src/pocketmine/item/PaintingItem.php - - - message: "#^Parameter \\#2 \\$level of static method pocketmine\\\\entity\\\\Entity\\:\\:createEntity\\(\\) expects pocketmine\\\\level\\\\Level, pocketmine\\\\level\\\\Level\\|null given\\.$#" - count: 1 - path: ../../../src/pocketmine/item/PaintingItem.php - - - - message: "#^Cannot call method broadcastLevelEvent\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/item/PaintingItem.php - - message: "#^Parameter \\#1 \\$effectType of class pocketmine\\\\entity\\\\EffectInstance constructor expects pocketmine\\\\entity\\\\Effect, pocketmine\\\\entity\\\\Effect\\|null given\\.$#" count: 1 @@ -1615,16 +1055,6 @@ parameters: count: 32 path: ../../../src/pocketmine/item/Potion.php - - - message: "#^Parameter \\#2 \\$level of static method pocketmine\\\\entity\\\\Entity\\:\\:createEntity\\(\\) expects pocketmine\\\\level\\\\Level, pocketmine\\\\level\\\\Level\\|null given\\.$#" - count: 1 - path: ../../../src/pocketmine/item/ProjectileItem.php - - - - message: "#^Cannot call method broadcastLevelSoundEvent\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/item/ProjectileItem.php - - message: "#^Parameter \\#1 \\$effectType of class pocketmine\\\\entity\\\\EffectInstance constructor expects pocketmine\\\\entity\\\\Effect, pocketmine\\\\entity\\\\Effect\\|null given\\.$#" count: 3 @@ -1640,11 +1070,6 @@ parameters: count: 1 path: ../../../src/pocketmine/item/RottenFlesh.php - - - message: "#^Parameter \\#2 \\$level of static method pocketmine\\\\entity\\\\Entity\\:\\:createEntity\\(\\) expects pocketmine\\\\level\\\\Level, pocketmine\\\\level\\\\Level\\|null given\\.$#" - count: 1 - path: ../../../src/pocketmine/item/SpawnEgg.php - - message: "#^Parameter \\#1 \\$effectType of class pocketmine\\\\entity\\\\EffectInstance constructor expects pocketmine\\\\entity\\\\Effect, pocketmine\\\\entity\\\\Effect\\|null given\\.$#" count: 1 @@ -1660,16 +1085,6 @@ parameters: count: 1 path: ../../../src/pocketmine/item/enchantment/EnchantmentList.php - - - message: "#^Property pocketmine\\\\level\\\\Explosion\\:\\:\\$level \\(pocketmine\\\\level\\\\Level\\) does not accept pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/level/Explosion.php - - - - message: "#^Parameter \\#1 \\$level of class pocketmine\\\\level\\\\utils\\\\SubChunkIteratorManager constructor expects pocketmine\\\\level\\\\ChunkManager, pocketmine\\\\level\\\\Level\\|null given\\.$#" - count: 1 - path: ../../../src/pocketmine/level/Explosion.php - - message: "#^Cannot call method getBlockId\\(\\) on pocketmine\\\\level\\\\format\\\\SubChunkInterface\\|null\\.$#" count: 1 @@ -1820,16 +1235,6 @@ parameters: count: 1 path: ../../../src/pocketmine/level/Level.php - - - message: "#^Cannot call method getName\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/level/Location.php - - - - message: "#^Cannot call method getName\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/level/Position.php - - message: "#^Method pocketmine\\\\level\\\\biome\\\\Biome\\:\\:getBiome\\(\\) should return pocketmine\\\\level\\\\biome\\\\Biome but returns pocketmine\\\\level\\\\biome\\\\Biome\\|null\\.$#" count: 1 @@ -2260,11 +1665,6 @@ parameters: count: 1 path: ../../../src/pocketmine/tile/Chest.php - - - message: "#^Cannot call method isInLoadedTerrain\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/tile/Chest.php - - message: "#^Parameter \\#1 \\$x of class pocketmine\\\\math\\\\Vector3 constructor expects float\\|int, int\\|null given\\.$#" count: 1 @@ -2276,7 +1676,12 @@ parameters: path: ../../../src/pocketmine/tile/Chest.php - - message: "#^Cannot call method getTileAt\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" + message: "#^Parameter \\#1 \\$x of method pocketmine\\\\level\\\\Level\\:\\:getTileAt\\(\\) expects int, int\\|null given\\.$#" + count: 1 + path: ../../../src/pocketmine/tile/Chest.php + + - + message: "#^Parameter \\#3 \\$z of method pocketmine\\\\level\\\\Level\\:\\:getTileAt\\(\\) expects int, int\\|null given\\.$#" count: 1 path: ../../../src/pocketmine/tile/Chest.php @@ -2290,11 +1695,6 @@ parameters: count: 1 path: ../../../src/pocketmine/tile/Furnace.php - - - message: "#^Cannot call method setBlock\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 2 - path: ../../../src/pocketmine/tile/Furnace.php - - message: "#^Cannot call method broadcastPacketToViewers\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" count: 1 @@ -2305,11 +1705,6 @@ parameters: count: 1 path: ../../../src/pocketmine/tile/Spawnable.php - - - message: "#^Cannot call method addTile\\(\\) on pocketmine\\\\level\\\\Level\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/tile/Tile.php - - message: "#^Cannot clone non\\-object variable \\$customBlockDataTag of type pocketmine\\\\nbt\\\\tag\\\\NamedTag\\|null\\.$#" count: 1 @@ -2370,11 +1765,6 @@ parameters: count: 1 path: ../../../src/pocketmine/utils/MainLogger.php - - - message: "#^Parameter \\#3 \\$subject of function preg_replace expects array\\|string, string\\|null given\\.$#" - count: 2 - path: ../../../src/pocketmine/utils/TextFormat.php - - message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\|string, string\\|null given\\.$#" count: 2 diff --git a/tests/phpunit/item/ItemTest.php b/tests/phpunit/item/ItemTest.php index d84449269..817840f6c 100644 --- a/tests/phpunit/item/ItemTest.php +++ b/tests/phpunit/item/ItemTest.php @@ -84,7 +84,7 @@ class ItemTest extends TestCase{ * @param int $meta */ public function testFromStringSingle(string $string, int $id, int $meta) : void{ - $item = ItemFactory::fromString($string); + $item = ItemFactory::fromStringSingle($string); self::assertEquals($id, $item->getId()); self::assertEquals($meta, $item->getDamage());