Merge branch 'next-minor' into stable

This commit is contained in:
Dylan K. Taylor 2020-06-04 19:59:46 +01:00
commit f495ba1d0b
145 changed files with 1278 additions and 1810 deletions

View File

@ -1,7 +1,6 @@
language: php language: php
php: php:
- 7.2
- 7.3 - 7.3
before_script: before_script:

View File

@ -2,7 +2,7 @@
## Pre-requisites ## Pre-requisites
- A bash shell (git bash is sufficient for Windows) - A bash shell (git bash is sufficient for Windows)
- [`git`](https://git-scm.com) available in your shell - [`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 - [`composer`](https://getcomposer.org) available in your shell
## Custom PHP binaries ## Custom PHP binaries

111
changelogs/3.13.md Normal file
View File

@ -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()`

View File

@ -5,7 +5,7 @@
"homepage": "https://pmmp.io", "homepage": "https://pmmp.io",
"license": "LGPL-3.0", "license": "LGPL-3.0",
"require": { "require": {
"php": ">=7.2.0", "php": ">=7.3.0",
"php-64bit": "*", "php-64bit": "*",
"ext-bcmath": "*", "ext-bcmath": "*",
"ext-curl": "*", "ext-curl": "*",
@ -34,7 +34,8 @@
"pocketmine/log": "^0.2.0", "pocketmine/log": "^0.2.0",
"pocketmine/log-pthreads": "^0.1.0", "pocketmine/log-pthreads": "^0.1.0",
"pocketmine/callback-validator": "^1.0.1", "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": { "require-dev": {
"phpstan/phpstan": "^0.12.25", "phpstan/phpstan": "^0.12.25",

55
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "59c415a2cb668990b4a456b4bd106881", "content-hash": "270aa35b0c4f554754b656129c5e8343",
"packages": [ "packages": [
{ {
"name": "adhocore/json-comment", "name": "adhocore/json-comment",
@ -50,6 +50,57 @@
], ],
"time": "2020-01-03T13:51:23+00:00" "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", "name": "pocketmine/binaryutils",
"version": "0.1.11", "version": "0.1.11",
@ -2091,7 +2142,7 @@
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {
"php": ">=7.2.0", "php": ">=7.3.0",
"php-64bit": "*", "php-64bit": "*",
"ext-bcmath": "*", "ext-bcmath": "*",
"ext-curl": "*", "ext-curl": "*",

View File

@ -23,13 +23,13 @@ declare(strict_types=1);
namespace pocketmine; namespace pocketmine;
use PackageVersions\Versions;
use pocketmine\network\mcpe\protocol\ProtocolInfo; use pocketmine\network\mcpe\protocol\ProtocolInfo;
use pocketmine\plugin\PluginBase; use pocketmine\plugin\PluginBase;
use pocketmine\plugin\PluginLoadOrder; use pocketmine\plugin\PluginLoadOrder;
use pocketmine\plugin\PluginManager; use pocketmine\plugin\PluginManager;
use pocketmine\utils\Utils; use pocketmine\utils\Utils;
use pocketmine\utils\VersionString; use pocketmine\utils\VersionString;
use raklib\RakLib;
use function base64_encode; use function base64_encode;
use function date; use function date;
use function error_get_last; use function error_get_last;
@ -88,7 +88,7 @@ class CrashDump{
* having their content changed, version format changing, etc. * having their content changed, version format changing, etc.
* It is not necessary to increase this when adding new fields. * 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_NONE = "none";
private const PLUGIN_INVOLVEMENT_DIRECT = "direct"; private const PLUGIN_INVOLVEMENT_DIRECT = "direct";
@ -117,10 +117,11 @@ class CrashDump{
mkdir($this->server->getDataPath() . "crashdumps"); mkdir($this->server->getDataPath() . "crashdumps");
} }
$this->path = $this->server->getDataPath() . "crashdumps/" . date("D_M_j-H.i.s-T_Y", $this->time) . ".log"; $this->path = $this->server->getDataPath() . "crashdumps/" . date("D_M_j-H.i.s-T_Y", $this->time) . ".log";
$this->fp = @fopen($this->path, "wb"); $fp = @fopen($this->path, "wb");
if(!is_resource($this->fp)){ if(!is_resource($fp)){
throw new \RuntimeException("Could not create Crash Dump"); throw new \RuntimeException("Could not create Crash Dump");
} }
$this->fp = $fp;
$this->data["format_version"] = self::FORMAT_VERSION; $this->data["format_version"] = self::FORMAT_VERSION;
$this->data["time"] = $this->time; $this->data["time"] = $this->time;
$this->addLine($this->server->getName() . " Crash Dump " . date("D M j H:i:s T Y", $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)){ if(isset($lastExceptionError)){
$error = $lastExceptionError; $error = $lastExceptionError;
}else{ }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 $error["trace"] = Utils::currentTrace(3); //Skipping CrashDump->baseCrash, CrashDump->construct, Server->crashDump
$errorConversion = [ $errorConversion = [
E_ERROR => "E_ERROR", 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"])){ if($this->server->getProperty("auto-report.send-code", true) !== false and file_exists($error["fullFile"])){
$file = @file($error["fullFile"], FILE_IGNORE_NEW_LINES); $file = @file($error["fullFile"], FILE_IGNORE_NEW_LINES);
for($l = max(0, $error["line"] - 10); $l < $error["line"] + 10 and isset($file[$l]); ++$l){ if($file !== false){
$this->addLine("[" . ($l + 1) . "] " . $file[$l]); for($l = max(0, $error["line"] - 10); $l < $error["line"] + 10 and isset($file[$l]); ++$l){
$this->data["code"][$l + 1] = $file[$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"]["is_dev"] = \pocketmine\IS_DEVELOPMENT_BUILD;
$this->data["general"]["protocol"] = ProtocolInfo::CURRENT_PROTOCOL; $this->data["general"]["protocol"] = ProtocolInfo::CURRENT_PROTOCOL;
$this->data["general"]["git"] = \pocketmine\GIT_COMMIT; $this->data["general"]["git"] = \pocketmine\GIT_COMMIT;
$this->data["general"]["raklib"] = RakLib::VERSION;
$this->data["general"]["uname"] = php_uname("a"); $this->data["general"]["uname"] = php_uname("a");
$this->data["general"]["php"] = phpversion(); $this->data["general"]["php"] = phpversion();
$this->data["general"]["zend"] = zend_version(); $this->data["general"]["zend"] = zend_version();
$this->data["general"]["php_os"] = PHP_OS; $this->data["general"]["php_os"] = PHP_OS;
$this->data["general"]["os"] = Utils::getOS(); $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($this->server->getName() . " version: " . $version->getFullVersion(true) . " [Protocol " . ProtocolInfo::CURRENT_PROTOCOL . "]");
$this->addLine("Git commit: " . \pocketmine\GIT_COMMIT); $this->addLine("Git commit: " . \pocketmine\GIT_COMMIT);
$this->addLine("uname -a: " . php_uname("a")); $this->addLine("uname -a: " . php_uname("a"));
$this->addLine("PHP Version: " . phpversion()); $this->addLine("PHP Version: " . phpversion());
$this->addLine("Zend version: " . zend_version()); $this->addLine("Zend version: " . zend_version());
$this->addLine("OS : " . PHP_OS . ", " . Utils::getOS()); $this->addLine("OS : " . PHP_OS . ", " . Utils::getOS());
$this->addLine("Composer libraries: ");
foreach(Versions::VERSIONS as $library => $libraryVersion){
$this->addLine("- $library $libraryVersion");
}
} }
/** /**

View File

@ -27,6 +27,8 @@ use pocketmine\event\server\LowMemoryEvent;
use pocketmine\scheduler\DumpWorkerMemoryTask; use pocketmine\scheduler\DumpWorkerMemoryTask;
use pocketmine\scheduler\GarbageCollectionTask; use pocketmine\scheduler\GarbageCollectionTask;
use pocketmine\timings\Timings; use pocketmine\timings\Timings;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Process;
use pocketmine\utils\Utils; use pocketmine\utils\Utils;
use function arsort; use function arsort;
use function count; use function count;
@ -225,7 +227,7 @@ class MemoryManager{
if(($this->memoryLimit > 0 or $this->globalMemoryLimit > 0) and ++$this->checkTicker >= $this->checkRate){ if(($this->memoryLimit > 0 or $this->globalMemoryLimit > 0) and ++$this->checkTicker >= $this->checkRate){
$this->checkTicker = 0; $this->checkTicker = 0;
$memory = Utils::getMemoryUsage(true); $memory = Process::getAdvancedMemoryUsage();
$trigger = false; $trigger = false;
if($this->memoryLimit > 0 and $memory[0] > $this->memoryLimit){ if($this->memoryLimit > 0 and $memory[0] > $this->memoryLimit){
$trigger = 0; $trigger = 0;
@ -304,6 +306,7 @@ class MemoryManager{
*/ */
public static function dumpMemory($startingObject, string $outputFolder, int $maxNesting, int $maxStringSize, \Logger $logger){ public static function dumpMemory($startingObject, string $outputFolder, int $maxNesting, int $maxStringSize, \Logger $logger){
$hardLimit = ini_get('memory_limit'); $hardLimit = ini_get('memory_limit');
if($hardLimit === false) throw new AssumptionFailedError("memory_limit INI directive should always exist");
ini_set('memory_limit', '-1'); ini_set('memory_limit', '-1');
gc_disable(); gc_disable();
@ -403,8 +406,8 @@ class MemoryManager{
"properties" => [] "properties" => []
]; ];
if($reflection->getParentClass()){ if(($parent = $reflection->getParentClass()) !== false){
$info["parent"] = $reflection->getParentClass()->getName(); $info["parent"] = $parent->getName();
} }
if(count($reflection->getInterfaceNames()) > 0){ if(count($reflection->getInterfaceNames()) > 0){

View File

@ -217,6 +217,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
public const SPECTATOR = 3; public const SPECTATOR = 3;
public const VIEW = Player::SPECTATOR; 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 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 */ /** @var bool[] map: raw UUID (string) => bool */
protected $hiddenPlayers = []; protected $hiddenPlayers = [];
/** @var float */
protected $moveRateLimit = 10 * self::MOVES_PER_TICK;
/** @var float|null */
protected $lastMovementProcess = null;
/** @var Vector3|null */ /** @var Vector3|null */
protected $newPosition; protected $forceMoveSync = null;
/** @var bool */
protected $isTeleporting = false;
/** @var int */ /** @var int */
protected $inAirTicks = 0; protected $inAirTicks = 0;
/** @var float */ /** @var float */
@ -524,7 +530,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
} }
public function spawnTo(Player $player) : void{ 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); parent::spawnTo($player);
} }
} }
@ -867,8 +873,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->lastPingMeasure = $pingMS; $this->lastPingMeasure = $pingMS;
} }
/**
* @deprecated
*/
public function getNextPosition() : Position{ public function getNextPosition() : Position{
return $this->newPosition !== null ? Position::fromObject($this->newPosition, $this->level) : $this->getPosition(); return $this->getPosition();
} }
public function getInAirTicks() : int{ 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. * Resets the player's cooldown time for the given item back to the maximum.
*/ */
public function resetItemCooldown(Item $item) : void{ public function resetItemCooldown(Item $item, ?int $ticks = null) : void{
$ticks = $item->getCooldownTicks(); $ticks = $ticks ?? $item->getCooldownTicks();
if($ticks > 0){ if($ticks > 0){
$this->usedItemsCooldown[$item->getId()] = $this->server->getTick() + $ticks; $this->usedItemsCooldown[$item->getId()] = $this->server->getTick() + $ticks;
} }
@ -1214,7 +1223,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
if(!($pos instanceof Position)){ if(!($pos instanceof Position)){
$level = $this->level; $level = $this->level;
}else{ }else{
$level = $pos->getLevel(); $level = $pos->getLevelNonNull();
} }
$this->spawnPosition = new Position($pos->x, $pos->y, $pos->z, $level); $this->spawnPosition = new Position($pos->x, $pos->y, $pos->z, $level);
$pk = new SetSpawnPositionPacket(); $pk = new SetSpawnPositionPacket();
@ -1530,23 +1539,19 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
} }
} }
/** protected function handleMovement(Vector3 $newPos) : void{
* @return void $this->moveRateLimit--;
*/ if($this->moveRateLimit < 0){
protected function processMovement(int $tickDiff){
if(!$this->isAlive() or !$this->spawned or $this->newPosition === null or $this->isSleeping()){
return; return;
} }
assert($this->x !== null and $this->y !== null and $this->z !== null); $oldPos = $this->asLocation();
assert($this->newPosition->x !== null and $this->newPosition->y !== null and $this->newPosition->z !== null); $distanceSquared = $newPos->distanceSquared($oldPos);
$newPos = $this->newPosition;
$distanceSquared = $newPos->distanceSquared($this);
$revert = false; $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 !!! /* !!! BEWARE YE WHO ENTER HERE !!!
* *
* This is NOT an anti-cheat check. It is a safety check. * 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. * 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($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; $revert = true;
}elseif(!$this->level->isInLoadedTerrain($newPos) or !$this->level->isChunkGenerated($newPos->getFloorX() >> 4, $newPos->getFloorZ() >> 4)){ }elseif(!$this->level->isInLoadedTerrain($newPos) or !$this->level->isChunkGenerated($newPos->getFloorX() >> 4, $newPos->getFloorZ() >> 4)){
$revert = true; $revert = true;
@ -1572,7 +1577,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->move($dx, $dy, $dz); $this->move($dx, $dy, $dz);
$diff = $this->distanceSquared($newPos) / $tickDiff ** 2; $diff = $this->distanceSquared($newPos);
if($this->isSurvival() and $diff > 0.0625){ if($this->isSurvival() and $diff > 0.0625){
$ev = new PlayerIllegalMoveEvent($this, $newPos, new Vector3($this->lastX, $this->lastY, $this->lastZ)); $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()){ if(!$ev->isCancelled()){
$revert = true; $revert = true;
$this->server->getLogger()->debug($this->getServer()->getLanguage()->translateString("pocketmine.player.invalidMove", [$this->getName()])); $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); $from = new Location($this->lastX, $this->lastY, $this->lastZ, $this->lastYaw, $this->lastPitch, $this->level);
$to = $this->getLocation(); $to = $this->getLocation();
$delta = (($this->lastX - $to->x) ** 2) + (($this->lastY - $to->y) ** 2) + (($this->lastZ - $to->z) ** 2); $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); $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->lastX = $to->x;
$this->lastY = $to->y; $this->lastY = $to->y;
$this->lastZ = $to->z; $this->lastZ = $to->z;
@ -1610,41 +1630,47 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$ev->call(); $ev->call();
if(!($revert = $ev->isCancelled())){ //Yes, this is intended if($ev->isCancelled()){
if($to->distanceSquared($ev->getTo()) > 0.01){ //If plugins modify the destination $this->revertMovement($from);
$this->teleport($ev->getTo()); return;
}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($revert){ if($to->distanceSquared($ev->getTo()) > 0.01){ //If plugins modify the destination
$this->teleport($ev->getTo());
return;
}
$this->lastX = $from->x; $this->broadcastMovement();
$this->lastY = $from->y;
$this->lastZ = $from->z;
$this->lastYaw = $from->yaw; $distance = sqrt((($from->x - $to->x) ** 2) + (($from->z - $to->z) ** 2));
$this->lastPitch = $from->pitch; //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); if($this->nextChunkOrderRun > 20){
$this->sendPosition($from, $from->yaw, $from->pitch, MovePlayerPacket::MODE_RESET);
}else{
if($distanceSquared != 0 and $this->nextChunkOrderRun > 20){
$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{ public function fall(float $fallDistance) : void{
@ -1716,7 +1742,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->timings->startTiming(); $this->timings->startTiming();
if($this->spawned){ 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) $this->motion->x = $this->motion->y = $this->motion->z = 0; //TODO: HACK! (Fixes player knockback being messed up)
if($this->onGround){ if($this->onGround){
$this->inAirTicks = 0; $this->inAirTicks = 0;
@ -1876,12 +1902,25 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$animations = []; $animations = [];
foreach($packet->clientData["AnimatedImageData"] as $animation){ 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 = []; $personaPieces = [];
foreach($packet->clientData["PersonaPieces"] as $piece){ 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 = []; $pieceTintColors = [];
@ -1892,9 +1931,17 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$skinData = new SkinData( $skinData = new SkinData(
$packet->clientData["SkinId"], $packet->clientData["SkinId"],
base64_decode($packet->clientData["SkinResourcePatch"] ?? "", true), 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, $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["SkinGeometryData"] ?? "", true),
base64_decode($packet->clientData["SkinAnimationData"] ?? "", true), base64_decode($packet->clientData["SkinAnimationData"] ?? "", true),
$packet->clientData["PremiumSkin"] ?? false, $packet->clientData["PremiumSkin"] ?? false,
@ -2251,8 +2298,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
public function handleMovePlayer(MovePlayerPacket $packet) : bool{ public function handleMovePlayer(MovePlayerPacket $packet) : bool{
$newPos = $packet->position->round(4)->subtract(0, $this->baseOffset, 0); $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 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->sendPosition($this, null, null, MovePlayerPacket::MODE_RESET);
$this->server->getLogger()->debug("Got outdated pre-teleport movement from " . $this->getName() . ", received " . $newPos . ", expected " . $this->asVector3()); $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 //Still getting movements from before teleport, ignore them
}elseif((!$this->isAlive() or !$this->spawned) and $newPos->distanceSquared($this) > 0.01){ }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()); $this->server->getLogger()->debug("Reverted movement of " . $this->getName() . " due to not alive or not spawned, received " . $newPos . ", locked at " . $this->asVector3());
}else{ }else{
// Once we get a movement within a reasonable distance, treat it as a teleport ACK and remove position lock // Once we get a movement within a reasonable distance, treat it as a teleport ACK and remove position lock
if($this->isTeleporting){ $this->forceMoveSync = null;
$this->isTeleporting = false;
}
$packet->yaw = fmod($packet->yaw, 360); $packet->yaw = fmod($packet->yaw, 360);
$packet->pitch = fmod($packet->pitch, 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->setRotation($packet->yaw, $packet->pitch);
$this->newPosition = $newPos; $this->handleMovement($newPos);
} }
return true; return true;
@ -2280,7 +2324,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
public function handleLevelSoundEvent(LevelSoundEventPacket $packet) : bool{ public function handleLevelSoundEvent(LevelSoundEventPacket $packet) : bool{
//TODO: add events so plugins can change this //TODO: add events so plugins can change this
$this->getLevel()->broadcastPacketToViewers($this, $packet); $this->getLevelNonNull()->broadcastPacketToViewers($this, $packet);
return true; return true;
} }
@ -2737,7 +2781,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$item = $block->getPickedItem(); $item = $block->getPickedItem();
if($packet->addUserData){ if($packet->addUserData){
$tile = $this->getLevel()->getTile($block); $tile = $this->getLevelNonNull()->getTile($block);
if($tile instanceof Tile){ if($tile instanceof Tile){
$nbt = $tile->getCleanedNBT(); $nbt = $tile->getCleanedNBT();
if($nbt instanceof CompoundTag){ 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 $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 $stay Duration in ticks to stay on screen for
@ -3278,28 +3323,55 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
* @return void * @return void
*/ */
public function addTitle(string $title, string $subtitle = "", int $fadeIn = -1, int $stay = -1, int $fadeOut = -1){ 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); $this->setTitleDuration($fadeIn, $stay, $fadeOut);
if($subtitle !== ""){ if($subtitle !== ""){
$this->addSubTitle($subtitle); $this->sendSubTitle($subtitle);
} }
$this->sendTitleText($title, SetTitlePacket::TYPE_SET_TITLE); $this->sendTitleText($title, SetTitlePacket::TYPE_SET_TITLE);
} }
/** /**
* Sets the subtitle message, without sending a title. * @deprecated
* @see Player::sendSubTitle()
* *
* @return void * @return void
*/ */
public function addSubTitle(string $subtitle){ 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); $this->sendTitleText($subtitle, SetTitlePacket::TYPE_SET_SUBTITLE);
} }
/** /**
* Adds small text to the user's screen. * @deprecated
* @see Player::sendActionBarMessage()
* *
* @return void * @return void
*/ */
public function addActionBarMessage(string $message){ 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); $this->sendTitleText($message, SetTitlePacket::TYPE_SET_ACTIONBAR_MESSAGE);
} }
@ -3598,7 +3670,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
} }
if($this->hasValidSpawnPosition()){ 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("SpawnX", $this->spawnPosition->getFloorX());
$this->namedtag->setInt("SpawnY", $this->spawnPosition->getFloorY()); $this->namedtag->setInt("SpawnY", $this->spawnPosition->getFloorY());
$this->namedtag->setInt("SpawnZ", $this->spawnPosition->getFloorZ()); $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. //main inventory and drops the rest on the ground.
$this->doCloseInventory(); $this->doCloseInventory();
$ev = new PlayerDeathEvent($this, $this->getDrops()); $ev = new PlayerDeathEvent($this, $this->getDrops(), null, $this->getXpDropAmount());
$ev->call(); $ev->call();
if(!$ev->getKeepInventory()){ 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, $ev->getXpDropAmount());
$this->level->dropExperience($this, $this->getXpDropAmount());
$this->setXpAndProgress(0, 0); $this->setXpAndProgress(0, 0);
if($ev->getDeathMessage() != ""){ if($ev->getDeathMessage() != ""){
@ -3682,7 +3753,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$ev = new PlayerRespawnEvent($this, $this->getSpawn()); $ev = new PlayerRespawnEvent($this, $this->getSpawn());
$ev->call(); $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->teleport($realSpawn);
$this->setSprinting(false); $this->setSprinting(false);
@ -3766,12 +3837,14 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$pk->onGround = $this->onGround; $pk->onGround = $this->onGround;
if($targets !== null){ if($targets !== null){
if(in_array($this, $targets, true)){
$this->forceMoveSync = $pos->asVector3();
}
$this->server->broadcastPacket($targets, $pk); $this->server->broadcastPacket($targets, $pk);
}else{ }else{
$this->forceMoveSync = $pos->asVector3();
$this->dataPacket($pk); $this->dataPacket($pk);
} }
$this->newPosition = null;
} }
/** /**
@ -3789,11 +3862,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->resetFallDistance(); $this->resetFallDistance();
$this->nextChunkOrderRun = 0; $this->nextChunkOrderRun = 0;
$this->newPosition = null;
$this->stopSleep(); $this->stopSleep();
$this->isTeleporting = true;
//TODO: workaround for player last pos not getting updated //TODO: workaround for player last pos not getting updated
//Entity::updateMovement() normally handles this, but it's overridden with an empty function in Player //Entity::updateMovement() normally handles this, but it's overridden with an empty function in Player
$this->resetLastMovements(); $this->resetLastMovements();

View File

@ -25,6 +25,7 @@ namespace pocketmine {
use pocketmine\utils\Git; use pocketmine\utils\Git;
use pocketmine\utils\MainLogger; use pocketmine\utils\MainLogger;
use pocketmine\utils\Process;
use pocketmine\utils\ServerKiller; use pocketmine\utils\ServerKiller;
use pocketmine\utils\Terminal; use pocketmine\utils\Terminal;
use pocketmine\utils\Timezone; use pocketmine\utils\Timezone;
@ -34,7 +35,7 @@ namespace pocketmine {
require_once __DIR__ . '/VersionInfo.php'; require_once __DIR__ . '/VersionInfo.php';
const MIN_PHP_VERSION = "7.2.0"; const MIN_PHP_VERSION = "7.3.0";
/** /**
* @param string $message * @param string $message
@ -279,7 +280,7 @@ namespace pocketmine {
if(ThreadManager::getInstance()->stopAll() > 0){ if(ThreadManager::getInstance()->stopAll() > 0){
$logger->debug("Some threads could not be stopped, performing a force-kill"); $logger->debug("Some threads could not be stopped, performing a force-kill");
Utils::kill(getmypid()); Process::kill(getmypid());
} }
}while(false); }while(false);

View File

@ -102,6 +102,7 @@ use pocketmine\updater\AutoUpdater;
use pocketmine\utils\Config; use pocketmine\utils\Config;
use pocketmine\utils\Internet; use pocketmine\utils\Internet;
use pocketmine\utils\MainLogger; use pocketmine\utils\MainLogger;
use pocketmine\utils\Process;
use pocketmine\utils\Terminal; use pocketmine\utils\Terminal;
use pocketmine\utils\TextFormat; use pocketmine\utils\TextFormat;
use pocketmine\utils\Utils; use pocketmine\utils\Utils;
@ -1665,7 +1666,7 @@ class Server{
} }
foreach($recipients as $recipient){ foreach($recipients as $recipient){
$recipient->addTitle($title, $subtitle, $fadeIn, $stay, $fadeOut); $recipient->sendTitle($title, $subtitle, $fadeIn, $stay, $fadeOut);
} }
return count($recipients); return count($recipients);
@ -1935,7 +1936,7 @@ class Server{
}catch(\Throwable $e){ }catch(\Throwable $e){
$this->logger->logException($e); $this->logger->logException($e);
$this->logger->emergency("Crashed while crashing, killing process"); $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; echo "--- Waiting $spacing seconds to throttle automatic restart (you can kill the process safely now) ---" . PHP_EOL;
sleep($spacing); sleep($spacing);
} }
@Utils::kill(getmypid()); @Process::kill(getmypid());
exit(1); exit(1);
} }
@ -2329,10 +2330,10 @@ class Server{
private function titleTick() : void{ private function titleTick() : void{
Timings::$titleTickTimer->startTiming(); Timings::$titleTickTimer->startTiming();
$d = Utils::getRealMemoryUsage(); $d = Process::getRealMemoryUsage();
$u = Utils::getMemoryUsage(true); $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), Utils::getThreadCount()); $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() . " " . echo "\x1b]0;" . $this->getName() . " " .
$this->getPocketMineVersion() . $this->getPocketMineVersion() .

View File

@ -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{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$direction = ($player !== null ? $player->getDirection() : 0) & 0x03; $direction = ($player !== null ? $player->getDirection() : 0) & 0x03;
$this->meta = $this->getVariant() | $direction; $this->meta = $this->getVariant() | $direction;
return $this->getLevel()->setBlock($blockReplace, $this, true, true); return $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
} }
} }

View File

@ -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{ 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(); $this->tryReconnect();
return true; return true;
} }
@ -279,7 +279,7 @@ abstract class BaseRail extends Flowable{
isset(self::ASCENDING_SIDES[$this->meta & 0x07]) and isset(self::ASCENDING_SIDES[$this->meta & 0x07]) and
$this->getSide(self::ASCENDING_SIDES[$this->meta & 0x07])->isTransparent() $this->getSide(self::ASCENDING_SIDES[$this->meta & 0x07])->isTransparent()
)){ )){
$this->getLevel()->useBreakOn($this); $this->getLevelNonNull()->useBreakOn($this);
} }
} }

View File

@ -83,7 +83,7 @@ class Bed extends Transparent{
$this->meta &= ~self::BITFLAG_OCCUPIED; $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){ if(($other = $this->getOtherHalf()) !== null and $other->isOccupied() !== $occupied){
$other->setOccupied($occupied); $other->setOccupied($occupied);
@ -137,7 +137,7 @@ class Bed extends Transparent{
return true; return true;
} }
$time = $this->getLevel()->getTime() % Level::TIME_FULL; $time = $this->getLevelNonNull()->getTimeOfDay();
$isNight = ($time >= Level::TIME_NIGHT and $time < Level::TIME_SUNRISE); $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; $meta = (($player instanceof Player ? $player->getDirection() : 0) - 1) & 0x03;
$next = $this->getSide(self::getOtherHalfSide($meta)); $next = $this->getSide(self::getOtherHalfSide($meta));
if($next->canBeReplaced() and !$next->getSide(Vector3::SIDE_DOWN)->isTransparent()){ if($next->canBeReplaced() and !$next->getSide(Vector3::SIDE_DOWN)->isTransparent()){
$this->getLevel()->setBlock($blockReplace, BlockFactory::get($this->id, $meta), true, true); $this->getLevelNonNull()->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($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->getLevelNonNull(), 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($next, $face, $item, $player));
return true; return true;
} }
@ -194,7 +194,7 @@ class Bed extends Transparent{
} }
private function getItem() : Item{ private function getItem() : Item{
$tile = $this->getLevel()->getTile($this); $tile = $this->getLevelNonNull()->getTile($this);
if($tile instanceof TileBed){ if($tile instanceof TileBed){
return ItemFactory::get($this->getItemId(), $tile->getColor()); return ItemFactory::get($this->getItemId(), $tile->getColor());
} }

View File

@ -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. * 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{ 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 * Do the actions needed so the block is broken with the Item
*/ */
public function onBreak(Item $item, Player $player = null) : bool{ 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){ public function getSide(int $side, int $step = 1){
if($this->isValid()){ 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))); return BlockFactory::get(Block::AIR, 0, Position::fromObject(Vector3::getSide($side, $step)));

View File

@ -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{ 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->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{ public function getVariantBitmask() : int{

View File

@ -68,18 +68,18 @@ class BurningFurnace extends Solid{
3 => 3 3 => 3
]; ];
$this->meta = $faces[$player instanceof Player ? $player->getDirection() : 0]; $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; return true;
} }
public function onActivate(Item $item, Player $player = null) : bool{ public function onActivate(Item $item, Player $player = null) : bool{
if($player instanceof Player){ if($player instanceof Player){
$furnace = $this->getLevel()->getTile($this); $furnace = $this->getLevelNonNull()->getTile($this);
if(!($furnace instanceof TileFurnace)){ 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)){ if(!($furnace instanceof TileFurnace)){
return true; return true;
} }

View File

@ -72,12 +72,12 @@ class Cactus extends Transparent{
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
$down = $this->getSide(Vector3::SIDE_DOWN); $down = $this->getSide(Vector3::SIDE_DOWN);
if($down->getId() !== self::SAND and $down->getId() !== self::CACTUS){ if($down->getId() !== self::SAND and $down->getId() !== self::CACTUS){
$this->getLevel()->useBreakOn($this); $this->getLevelNonNull()->useBreakOn($this);
}else{ }else{
for($side = 2; $side <= 5; ++$side){ for($side = 2; $side <= 5; ++$side){
$b = $this->getSide($side); $b = $this->getSide($side);
if($b->isSolid()){ if($b->isSolid()){
$this->getLevel()->useBreakOn($this); $this->getLevelNonNull()->useBreakOn($this);
break; break;
} }
} }
@ -92,23 +92,23 @@ class Cactus extends Transparent{
if($this->getSide(Vector3::SIDE_DOWN)->getId() !== self::CACTUS){ if($this->getSide(Vector3::SIDE_DOWN)->getId() !== self::CACTUS){
if($this->meta === 0x0f){ if($this->meta === 0x0f){
for($y = 1; $y < 3; ++$y){ 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){ if($b->getId() === self::AIR){
$ev = new BlockGrowEvent($b, BlockFactory::get(Block::CACTUS)); $ev = new BlockGrowEvent($b, BlockFactory::get(Block::CACTUS));
$ev->call(); $ev->call();
if($ev->isCancelled()){ if($ev->isCancelled()){
break; break;
} }
$this->getLevel()->setBlock($b, $ev->getNewState(), true); $this->getLevelNonNull()->setBlock($b, $ev->getNewState(), true);
}else{ }else{
break; break;
} }
} }
$this->meta = 0; $this->meta = 0;
$this->getLevel()->setBlock($this, $this); $this->getLevelNonNull()->setBlock($this, $this);
}else{ }else{
++$this->meta; ++$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); $block2 = $this->getSide(Vector3::SIDE_WEST);
$block3 = $this->getSide(Vector3::SIDE_EAST); $block3 = $this->getSide(Vector3::SIDE_EAST);
if(!$block0->isSolid() and !$block1->isSolid() and !$block2->isSolid() and !$block3->isSolid()){ 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; return true;
} }

View File

@ -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{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$down = $this->getSide(Vector3::SIDE_DOWN); $down = $this->getSide(Vector3::SIDE_DOWN);
if($down->getId() !== self::AIR){ if($down->getId() !== self::AIR){
$this->getLevel()->setBlock($blockReplace, $this, true, true); $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true; return true;
} }
@ -76,7 +76,7 @@ class Cake extends Transparent implements FoodSource{
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->getId() === self::AIR){ //Replace with common break method 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);
} }
} }

View File

@ -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{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$down = $this->getSide(Vector3::SIDE_DOWN); $down = $this->getSide(Vector3::SIDE_DOWN);
if($down->getId() !== self::AIR){ if($down->getId() !== self::AIR){
$this->getLevel()->setBlock($blockReplace, $this, true, true); $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true; return true;
} }
@ -74,7 +74,7 @@ class Carpet extends Flowable{
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->getId() === self::AIR){ if($this->getSide(Vector3::SIDE_DOWN)->getId() === self::AIR){
$this->getLevel()->useBreakOn($this); $this->getLevelNonNull()->useBreakOn($this);
} }
} }

View File

@ -81,7 +81,7 @@ class Chest extends Transparent{
} }
$c = $this->getSide($side); $c = $this->getSide($side);
if($c->getId() === $this->id and $c->getDamage() === $this->meta){ 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()){ if($tile instanceof TileChest and !$tile->isPaired()){
$chest = $tile; $chest = $tile;
break; break;
@ -89,8 +89,8 @@ class Chest extends Transparent{
} }
} }
$this->getLevel()->setBlock($blockReplace, $this, true, true); $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
$tile = Tile::createTile(Tile::CHEST, $this->getLevel(), TileChest::createNBT($this, $face, $item, $player)); $tile = Tile::createTile(Tile::CHEST, $this->getLevelNonNull(), TileChest::createNBT($this, $face, $item, $player));
if($chest instanceof TileChest and $tile instanceof TileChest){ if($chest instanceof TileChest and $tile instanceof TileChest){
$chest->pairWith($tile); $chest->pairWith($tile);
@ -103,12 +103,12 @@ class Chest extends Transparent{
public function onActivate(Item $item, Player $player = null) : bool{ public function onActivate(Item $item, Player $player = null) : bool{
if($player instanceof Player){ if($player instanceof Player){
$t = $this->getLevel()->getTile($this); $t = $this->getLevelNonNull()->getTile($this);
$chest = null; $chest = null;
if($t instanceof TileChest){ if($t instanceof TileChest){
$chest = $t; $chest = $t;
}else{ }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)){ if(!($chest instanceof TileChest)){
return true; return true;
} }

View File

@ -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{ 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){ 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; return true;
} }
@ -52,7 +52,7 @@ abstract class Crops extends Flowable{
$ev = new BlockGrowEvent($this, $block); $ev = new BlockGrowEvent($this, $block);
$ev->call(); $ev->call();
if(!$ev->isCancelled()){ if(!$ev->isCancelled()){
$this->getLevel()->setBlock($this, $ev->getNewState(), true, true); $this->getLevelNonNull()->setBlock($this, $ev->getNewState(), true, true);
} }
$item->pop(); $item->pop();
@ -65,7 +65,7 @@ abstract class Crops extends Flowable{
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->getId() !== Block::FARMLAND){ 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 = new BlockGrowEvent($this, $block);
$ev->call(); $ev->call();
if(!$ev->isCancelled()){ if(!$ev->isCancelled()){
$this->getLevel()->setBlock($this, $ev->getNewState(), true, true); $this->getLevelNonNull()->setBlock($this, $ev->getNewState(), true, true);
} }
} }
} }

View File

@ -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{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$down = $this->getSide(Vector3::SIDE_DOWN); $down = $this->getSide(Vector3::SIDE_DOWN);
if($down->getId() === Block::GRASS or $down->getId() === Block::DIRT or $down->getId() === Block::FARMLAND){ 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; return true;
} }
@ -52,7 +52,7 @@ class Dandelion extends Flowable{
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){ if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){
$this->getLevel()->useBreakOn($this); $this->getLevelNonNull()->useBreakOn($this);
} }
} }

View File

@ -51,7 +51,7 @@ class DeadBush extends Flowable{
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){ if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){
$this->getLevel()->useBreakOn($this); $this->getLevelNonNull()->useBreakOn($this);
} }
} }

View File

@ -54,9 +54,9 @@ class Dirt extends Solid{
if($item instanceof Hoe){ if($item instanceof Hoe){
$item->applyDamage(1); $item->applyDamage(1);
if($this->meta === 1){ if($this->meta === 1){
$this->getLevel()->setBlock($this, BlockFactory::get(Block::DIRT), true); $this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::DIRT), true);
}else{ }else{
$this->getLevel()->setBlock($this, BlockFactory::get(Block::FARMLAND), true); $this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::FARMLAND), true);
} }
return true; return true;

View File

@ -201,9 +201,9 @@ abstract class Door extends Transparent{
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->getId() === self::AIR){ //Replace with common break method 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){ 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->setDamage($player->getDirection() & 0x03);
$this->getLevel()->setBlock($blockReplace, $this, true, true); //Bottom $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true); //Bottom
$this->getLevel()->setBlock($blockUp, BlockFactory::get($this->getId(), $metaUp), true); //Top $this->getLevelNonNull()->setBlock($blockUp, BlockFactory::get($this->getId(), $metaUp), true); //Top
return true; return true;
} }

View File

@ -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{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$id = $blockReplace->getSide(Vector3::SIDE_DOWN)->getId(); $id = $blockReplace->getSide(Vector3::SIDE_DOWN)->getId();
if(($id === Block::GRASS or $id === Block::DIRT) and $blockReplace->getSide(Vector3::SIDE_UP)->canBeReplaced()){ if(($id === Block::GRASS or $id === Block::DIRT) and $blockReplace->getSide(Vector3::SIDE_UP)->canBeReplaced()){
$this->getLevel()->setBlock($blockReplace, $this, false, false); $this->getLevelNonNull()->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->getSide(Vector3::SIDE_UP), BlockFactory::get($this->id, $this->meta | self::BITFLAG_TOP), false, false);
return true; return true;
} }
@ -85,7 +85,7 @@ class DoublePlant extends Flowable{
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if(!$this->isValidHalfPlant() or (($this->meta & self::BITFLAG_TOP) === 0 and $this->getSide(Vector3::SIDE_DOWN)->isTransparent())){ 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);
} }
} }

View File

@ -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{ 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; return true;
} }

View File

@ -69,8 +69,8 @@ class EnderChest extends Chest{
$this->meta = $faces[$player instanceof Player ? $player->getDirection() : 0]; $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::ENDER_CHEST, $this->getLevel(), TileEnderChest::createNBT($this, $face, $item, $player)); Tile::createTile(Tile::ENDER_CHEST, $this->getLevelNonNull(), TileEnderChest::createNBT($this, $face, $item, $player));
return true; return true;
} }
@ -78,12 +78,12 @@ class EnderChest extends Chest{
public function onActivate(Item $item, Player $player = null) : bool{ public function onActivate(Item $item, Player $player = null) : bool{
if($player instanceof Player){ if($player instanceof Player){
$t = $this->getLevel()->getTile($this); $t = $this->getLevelNonNull()->getTile($this);
$enderChest = null; $enderChest = null;
if($t instanceof TileEnderChest){ if($t instanceof TileEnderChest){
$enderChest = $t; $enderChest = $t;
}else{ }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)){ if(!($enderChest instanceof TileEnderChest)){
return true; return true;
} }

View File

@ -37,7 +37,7 @@ abstract class Fallable extends Solid{
$nbt->setInt("TileID", $this->getId()); $nbt->setInt("TileID", $this->getId());
$nbt->setByte("Data", $this->getDamage()); $nbt->setByte("Data", $this->getDamage());
$fall = Entity::createEntity("FallingSand", $this->getLevel(), $nbt); $fall = Entity::createEntity("FallingSand", $this->getLevelNonNull(), $nbt);
if($fall !== null){ if($fall !== null){
$fall->spawnToAll(); $fall->spawnToAll();

View File

@ -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{ 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->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; return true;
} }
@ -85,7 +85,7 @@ class FenceGate extends Transparent{
$this->meta |= (($player->getDirection() - 1) & 0x02); $this->meta |= (($player->getDirection() - 1) & 0x02);
} }
$this->getLevel()->setBlock($this, $this, true); $this->getLevelNonNull()->setBlock($this, $this, true);
$this->level->addSound(new DoorSound($this)); $this->level->addSound(new DoorSound($this));
return true; return true;
} }

View File

@ -82,7 +82,7 @@ class Fire extends Flowable{
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if(!$this->getSide(Vector3::SIDE_DOWN)->isSolid() and !$this->hasAdjacentFlammableBlocks()){ 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{ }else{
$this->level->scheduleDelayedBlockUpdate($this, mt_rand(30, 40)); $this->level->scheduleDelayedBlockUpdate($this, mt_rand(30, 40));
} }

View File

@ -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{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$down = $this->getSide(Vector3::SIDE_DOWN); $down = $this->getSide(Vector3::SIDE_DOWN);
if($down->getId() === Block::GRASS or $down->getId() === Block::DIRT or $down->getId() === Block::FARMLAND){ 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; return true;
} }
@ -72,7 +72,7 @@ class Flower extends Flowable{
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){ if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){
$this->getLevel()->useBreakOn($this); $this->getLevelNonNull()->useBreakOn($this);
} }
} }

View File

@ -62,19 +62,19 @@ class FlowerPot extends Flowable{
return false; return false;
} }
$this->getLevel()->setBlock($blockReplace, $this, true, true); $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
Tile::createTile(Tile::FLOWER_POT, $this->getLevel(), TileFlowerPot::createNBT($this, $face, $item, $player)); Tile::createTile(Tile::FLOWER_POT, $this->getLevelNonNull(), TileFlowerPot::createNBT($this, $face, $item, $player));
return true; return true;
} }
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){ if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){
$this->getLevel()->useBreakOn($this); $this->getLevelNonNull()->useBreakOn($this);
} }
} }
public function onActivate(Item $item, Player $player = null) : bool{ public function onActivate(Item $item, Player $player = null) : bool{
$pot = $this->getLevel()->getTile($this); $pot = $this->getLevelNonNull()->getTile($this);
if(!($pot instanceof TileFlowerPot)){ if(!($pot instanceof TileFlowerPot)){
return false; 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->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()); $pot->setItem($item->pop());
return true; return true;
@ -96,7 +96,7 @@ class FlowerPot extends Flowable{
public function getDropsForCompatibleTool(Item $item) : array{ public function getDropsForCompatibleTool(Item $item) : array{
$items = parent::getDropsForCompatibleTool($item); $items = parent::getDropsForCompatibleTool($item);
$tile = $this->getLevel()->getTile($this); $tile = $this->getLevelNonNull()->getTile($this);
if($tile instanceof TileFlowerPot){ if($tile instanceof TileFlowerPot){
$item = $tile->getItem(); $item = $tile->getItem();
if($item->getId() !== Item::AIR){ if($item->getId() !== Item::AIR){

View File

@ -53,7 +53,7 @@ class GlazedTerracotta extends Solid{
$this->meta = $faces[(~($player->getDirection() - 1)) & 0x03]; $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{ public function getVariantBitmask() : int{

View File

@ -53,6 +53,6 @@ class GlowingRedstoneOre extends RedstoneOre{
} }
public function onRandomTick() : void{ 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);
} }
} }

View File

@ -100,17 +100,17 @@ class Grass extends Solid{
public function onActivate(Item $item, Player $player = null) : bool{ public function onActivate(Item $item, Player $player = null) : bool{
if($item->getId() === Item::DYE and $item->getDamage() === 0x0F){ if($item->getId() === Item::DYE and $item->getDamage() === 0x0F){
$item->pop(); $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; return true;
}elseif($item instanceof Hoe){ }elseif($item instanceof Hoe){
$item->applyDamage(1); $item->applyDamage(1);
$this->getLevel()->setBlock($this, BlockFactory::get(Block::FARMLAND)); $this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::FARMLAND));
return true; return true;
}elseif($item instanceof Shovel and $this->getSide(Vector3::SIDE_UP)->getId() === Block::AIR){ }elseif($item instanceof Shovel and $this->getSide(Vector3::SIDE_UP)->getId() === Block::AIR){
$item->applyDamage(1); $item->applyDamage(1);
$this->getLevel()->setBlock($this, BlockFactory::get(Block::GRASS_PATH)); $this->getLevelNonNull()->setBlock($this, BlockFactory::get(Block::GRASS_PATH));
return true; return true;
} }

View File

@ -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{ 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->meta = PillarRotationHelper::getMetaFromFace($this->meta, $face);
$this->getLevel()->setBlock($blockReplace, $this, true, true); $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true; return true;
} }

View File

@ -57,7 +57,7 @@ class Ice extends Transparent{
public function onBreak(Item $item, Player $player = null) : bool{ public function onBreak(Item $item, Player $player = null) : bool{
if(($player === null or $player->isSurvival()) and !$item->hasEnchantment(Enchantment::SILK_TOUCH)){ 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); return parent::onBreak($item, $player);
} }

View File

@ -46,7 +46,7 @@ class ItemFrame extends Flowable{
public function onActivate(Item $item, Player $player = null) : bool{ public function onActivate(Item $item, Player $player = null) : bool{
$tile = $this->level->getTile($this); $tile = $this->level->getTile($this);
if(!($tile instanceof TileItemFrame)){ 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)){ if(!($tile instanceof TileItemFrame)){
return true; return true;
} }
@ -88,7 +88,7 @@ class ItemFrame extends Flowable{
$this->meta = $faces[$face]; $this->meta = $faces[$face];
$this->level->setBlock($blockReplace, $this, true, true); $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; return true;

View File

@ -100,7 +100,7 @@ class Ladder extends Transparent{
]; ];
if(isset($faces[$face])){ if(isset($faces[$face])){
$this->meta = $faces[$face]; $this->meta = $faces[$face];
$this->getLevel()->setBlock($blockReplace, $this, true, true); $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true; return true;
} }

View File

@ -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{ 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); $ret = $this->getLevelNonNull()->setBlock($this, $this, true, false);
$this->getLevel()->scheduleDelayedBlockUpdate($this, $this->tickRate()); $this->getLevelNonNull()->scheduleDelayedBlockUpdate($this, $this->tickRate());
return $ret; return $ret;
} }

View File

@ -139,7 +139,7 @@ class Leaves extends Transparent{
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if(($this->meta & 0b00001100) === 0){ if(($this->meta & 0b00001100) === 0){
$this->meta |= 0x08; $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 = new LeavesDecayEvent($this);
$ev->call(); $ev->call();
if($ev->isCancelled() or $this->findLog($this, $visited, 0)){ if($ev->isCancelled() or $this->findLog($this, $visited, 0)){
$this->getLevel()->setBlock($this, $this, false, false); $this->getLevelNonNull()->setBlock($this, $this, false, false);
}else{ }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{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$this->meta |= 0x04; $this->meta |= 0x04;
return $this->getLevel()->setBlock($this, $this, true); return $this->getLevelNonNull()->setBlock($this, $this, true);
} }
public function getVariantBitmask() : int{ public function getVariantBitmask() : int{

View File

@ -49,7 +49,7 @@ class MelonStem extends Crops{
$ev = new BlockGrowEvent($this, $block); $ev = new BlockGrowEvent($this, $block);
$ev->call(); $ev->call();
if(!$ev->isCancelled()){ if(!$ev->isCancelled()){
$this->getLevel()->setBlock($this, $ev->getNewState(), true); $this->getLevelNonNull()->setBlock($this, $ev->getNewState(), true);
} }
}else{ }else{
for($side = 2; $side <= 5; ++$side){ for($side = 2; $side <= 5; ++$side){
@ -64,7 +64,7 @@ class MelonStem extends Crops{
$ev = new BlockGrowEvent($side, BlockFactory::get(Block::MELON_BLOCK)); $ev = new BlockGrowEvent($side, BlockFactory::get(Block::MELON_BLOCK));
$ev->call(); $ev->call();
if(!$ev->isCancelled()){ if(!$ev->isCancelled()){
$this->getLevel()->setBlock($side, $ev->getNewState(), true); $this->getLevelNonNull()->setBlock($side, $ev->getNewState(), true);
} }
} }
} }

View File

@ -64,13 +64,13 @@ class Mycelium extends Solid{
$x = mt_rand($this->x - 1, $this->x + 1); $x = mt_rand($this->x - 1, $this->x + 1);
$y = mt_rand($this->y - 2, $this->y + 2); $y = mt_rand($this->y - 2, $this->y + 2);
$z = mt_rand($this->z - 1, $this->z + 1); $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->getId() === Block::DIRT){
if($block->getSide(Vector3::SIDE_UP) instanceof Transparent){ if($block->getSide(Vector3::SIDE_UP) instanceof Transparent){
$ev = new BlockSpreadEvent($block, $this, BlockFactory::get(Block::MYCELIUM)); $ev = new BlockSpreadEvent($block, $this, BlockFactory::get(Block::MYCELIUM));
$ev->call(); $ev->call();
if(!$ev->isCancelled()){ if(!$ev->isCancelled()){
$this->getLevel()->setBlock($block, $ev->getNewState()); $this->getLevelNonNull()->setBlock($block, $ev->getNewState());
} }
} }
} }

View File

@ -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{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$down = $this->getSide(Vector3::SIDE_DOWN); $down = $this->getSide(Vector3::SIDE_DOWN);
if($down->getId() === Block::SOUL_SAND){ if($down->getId() === Block::SOUL_SAND){
$this->getLevel()->setBlock($blockReplace, $this, false, true); $this->getLevelNonNull()->setBlock($blockReplace, $this, false, true);
return true; return true;
} }
@ -56,7 +56,7 @@ class NetherWartPlant extends Flowable{
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->getId() !== Block::SOUL_SAND){ 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 = new BlockGrowEvent($this, $block);
$ev->call(); $ev->call();
if(!$ev->isCancelled()){ if(!$ev->isCancelled()){
$this->getLevel()->setBlock($this, $ev->getNewState(), false, true); $this->getLevelNonNull()->setBlock($this, $ev->getNewState(), false, true);
} }
} }
} }

View File

@ -51,7 +51,7 @@ class Pumpkin extends Solid{
if($player instanceof Player){ if($player instanceof Player){
$this->meta = ((int) $player->getDirection() + 1) % 4; $this->meta = ((int) $player->getDirection() + 1) % 4;
} }
$this->getLevel()->setBlock($blockReplace, $this, true, true); $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true; return true;
} }

View File

@ -49,7 +49,7 @@ class PumpkinStem extends Crops{
$ev = new BlockGrowEvent($this, $block); $ev = new BlockGrowEvent($this, $block);
$ev->call(); $ev->call();
if(!$ev->isCancelled()){ if(!$ev->isCancelled()){
$this->getLevel()->setBlock($this, $ev->getNewState(), true); $this->getLevelNonNull()->setBlock($this, $ev->getNewState(), true);
} }
}else{ }else{
for($side = 2; $side <= 5; ++$side){ for($side = 2; $side <= 5; ++$side){
@ -64,7 +64,7 @@ class PumpkinStem extends Crops{
$ev = new BlockGrowEvent($side, BlockFactory::get(Block::PUMPKIN)); $ev = new BlockGrowEvent($side, BlockFactory::get(Block::PUMPKIN));
$ev->call(); $ev->call();
if(!$ev->isCancelled()){ if(!$ev->isCancelled()){
$this->getLevel()->setBlock($side, $ev->getNewState(), true); $this->getLevelNonNull()->setBlock($side, $ev->getNewState(), true);
} }
} }
} }

View File

@ -58,7 +58,7 @@ class Quartz extends Solid{
if($this->getVariant() !== self::NORMAL){ if($this->getVariant() !== self::NORMAL){
$this->meta = PillarRotationHelper::getMetaFromFace($this->meta, $face); $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{ public function getToolType() : int{

View File

@ -45,14 +45,14 @@ class RedMushroom extends Flowable{
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){ 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{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$down = $this->getSide(Vector3::SIDE_DOWN); $down = $this->getSide(Vector3::SIDE_DOWN);
if(!$down->isTransparent()){ if(!$down->isTransparent()){
$this->getLevel()->setBlock($blockReplace, $this, true, true); $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true; return true;
} }

View File

@ -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{ 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{ 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 return false; //this shouldn't prevent block placement
} }
public function onNearbyBlockChange() : void{ 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{ public function getToolType() : int{

View File

@ -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{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$down = $this->getSide(Vector3::SIDE_DOWN); $down = $this->getSide(Vector3::SIDE_DOWN);
if($down->getId() === self::GRASS or $down->getId() === self::DIRT or $down->getId() === self::FARMLAND){ 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; return true;
} }
@ -70,7 +70,7 @@ class Sapling extends Flowable{
public function onActivate(Item $item, Player $player = null) : bool{ public function onActivate(Item $item, Player $player = null) : bool{
if($item->getId() === Item::DYE and $item->getDamage() === 0x0F){ //Bonemeal if($item->getId() === Item::DYE and $item->getDamage() === 0x0F){ //Bonemeal
//TODO: change log type //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(); $item->pop();
@ -82,7 +82,7 @@ class Sapling extends Flowable{
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){ 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{ public function onRandomTick() : void{
if($this->level->getFullLightAt($this->x, $this->y, $this->z) >= 8 and mt_rand(1, 7) === 1){ if($this->level->getFullLightAt($this->x, $this->y, $this->z) >= 8 and mt_rand(1, 7) === 1){
if(($this->meta & 0x08) === 0x08){ 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{ }else{
$this->meta |= 0x08; $this->meta |= 0x08;
$this->getLevel()->setBlock($this, $this, true); $this->getLevelNonNull()->setBlock($this, $this, true);
} }
} }
} }

View File

@ -62,13 +62,13 @@ class SignPost extends Transparent{
if($face === Vector3::SIDE_UP){ if($face === Vector3::SIDE_UP){
$this->meta = $player !== null ? (floor((($player->yaw + 180) * 16 / 360) + 0.5) & 0x0f) : 0; $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{ }else{
$this->meta = $face; $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; return true;
} }
@ -78,7 +78,7 @@ class SignPost extends Transparent{
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->getId() === self::AIR){ if($this->getSide(Vector3::SIDE_DOWN)->getId() === self::AIR){
$this->getLevel()->useBreakOn($this); $this->getLevelNonNull()->useBreakOn($this);
} }
} }

View File

@ -65,8 +65,8 @@ class Skull extends Flowable{
} }
$this->meta = $face; $this->meta = $face;
$this->getLevel()->setBlock($blockReplace, $this, true); $this->getLevelNonNull()->setBlock($blockReplace, $this, true);
Tile::createTile(Tile::SKULL, $this->getLevel(), TileSkull::createNBT($this, $face, $item, $player)); Tile::createTile(Tile::SKULL, $this->getLevelNonNull(), TileSkull::createNBT($this, $face, $item, $player));
return true; return true;
} }

View File

@ -56,11 +56,11 @@ abstract class Slab extends Transparent{
$this->meta &= 0x07; $this->meta &= 0x07;
if($face === Vector3::SIDE_DOWN){ if($face === Vector3::SIDE_DOWN){
if($blockClicked->getId() === $this->id and ($blockClicked->getDamage() & 0x08) === 0x08 and $blockClicked->getVariant() === $this->getVariant()){ 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; return true;
}elseif($blockReplace->getId() === $this->id and $blockReplace->getVariant() === $this->getVariant()){ }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; return true;
}else{ }else{
@ -68,18 +68,18 @@ abstract class Slab extends Transparent{
} }
}elseif($face === Vector3::SIDE_UP){ }elseif($face === Vector3::SIDE_UP){
if($blockClicked->getId() === $this->id and ($blockClicked->getDamage() & 0x08) === 0 and $blockClicked->getVariant() === $this->getVariant()){ 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; return true;
}elseif($blockReplace->getId() === $this->id and $blockReplace->getVariant() === $this->getVariant()){ }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; return true;
} }
}else{ //TODO: collision }else{ //TODO: collision
if($blockReplace->getId() === $this->id){ if($blockReplace->getId() === $this->id){
if($blockReplace->getVariant() === $this->getVariant()){ 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; return true;
} }
@ -95,7 +95,7 @@ abstract class Slab extends Transparent{
if($blockReplace->getId() === $this->id and $blockClicked->getVariant() !== $this->getVariant()){ if($blockReplace->getId() === $this->id and $blockClicked->getVariant() !== $this->getVariant()){
return false; return false;
} }
$this->getLevel()->setBlock($blockReplace, $this, true, true); $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true; return true;
} }

View File

@ -66,7 +66,7 @@ class SnowLayer extends Flowable{
$this->setDamage($blockReplace->getDamage() + 1); $this->setDamage($blockReplace->getDamage() + 1);
} }
if($this->canBeSupportedBy($blockReplace->getSide(Vector3::SIDE_DOWN))){ if($this->canBeSupportedBy($blockReplace->getSide(Vector3::SIDE_DOWN))){
$this->getLevel()->setBlock($blockReplace, $this, true); $this->getLevelNonNull()->setBlock($blockReplace, $this, true);
return true; return true;
} }
@ -76,7 +76,7 @@ class SnowLayer extends Flowable{
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedBy($this->getSide(Vector3::SIDE_DOWN))){ 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{ public function onRandomTick() : void{
if($this->level->getBlockLightAt($this->x, $this->y, $this->z) >= 12){ 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);
} }
} }

View File

@ -93,7 +93,7 @@ abstract class Stair extends Transparent{
if(($clickVector->y > 0.5 and $face !== Vector3::SIDE_UP) or $face === Vector3::SIDE_DOWN){ if(($clickVector->y > 0.5 and $face !== Vector3::SIDE_UP) or $face === Vector3::SIDE_DOWN){
$this->meta |= 0x04; //Upside-down stairs $this->meta |= 0x04; //Upside-down stairs
} }
$this->getLevel()->setBlock($blockReplace, $this, true, true); $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true; return true;
} }

View File

@ -62,13 +62,13 @@ class StandingBanner extends Transparent{
if($face !== Vector3::SIDE_DOWN){ if($face !== Vector3::SIDE_DOWN){
if($face === Vector3::SIDE_UP and $player !== null){ if($face === Vector3::SIDE_UP and $player !== null){
$this->meta = floor((($player->yaw + 180) * 16 / 360) + 0.5) & 0x0f; $this->meta = floor((($player->yaw + 180) * 16 / 360) + 0.5) & 0x0f;
$this->getLevel()->setBlock($blockReplace, $this, true); $this->getLevelNonNull()->setBlock($blockReplace, $this, true);
}else{ }else{
$this->meta = $face; $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; return true;
} }
@ -77,7 +77,7 @@ class StandingBanner extends Transparent{
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->getId() === self::AIR){ if($this->getSide(Vector3::SIDE_DOWN)->getId() === self::AIR){
$this->getLevel()->useBreakOn($this); $this->getLevelNonNull()->useBreakOn($this);
} }
} }

View File

@ -46,20 +46,20 @@ class Sugarcane extends Flowable{
if($item->getId() === Item::DYE and $item->getDamage() === 0x0F){ //Bonemeal if($item->getId() === Item::DYE and $item->getDamage() === 0x0F){ //Bonemeal
if($this->getSide(Vector3::SIDE_DOWN)->getId() !== self::SUGARCANE_BLOCK){ if($this->getSide(Vector3::SIDE_DOWN)->getId() !== self::SUGARCANE_BLOCK){
for($y = 1; $y < 3; ++$y){ 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){ if($b->getId() === self::AIR){
$ev = new BlockGrowEvent($b, BlockFactory::get(Block::SUGARCANE_BLOCK)); $ev = new BlockGrowEvent($b, BlockFactory::get(Block::SUGARCANE_BLOCK));
$ev->call(); $ev->call();
if($ev->isCancelled()){ if($ev->isCancelled()){
break; break;
} }
$this->getLevel()->setBlock($b, $ev->getNewState(), true); $this->getLevelNonNull()->setBlock($b, $ev->getNewState(), true);
}else{ }else{
break; break;
} }
} }
$this->meta = 0; $this->meta = 0;
$this->getLevel()->setBlock($this, $this, true); $this->getLevelNonNull()->setBlock($this, $this, true);
} }
$item->pop(); $item->pop();
@ -73,7 +73,7 @@ class Sugarcane extends Flowable{
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
$down = $this->getSide(Vector3::SIDE_DOWN); $down = $this->getSide(Vector3::SIDE_DOWN);
if($down->isTransparent() and $down->getId() !== self::SUGARCANE_BLOCK){ 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->getSide(Vector3::SIDE_DOWN)->getId() !== self::SUGARCANE_BLOCK){
if($this->meta === 0x0F){ if($this->meta === 0x0F){
for($y = 1; $y < 3; ++$y){ 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){ 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; break;
} }
} }
$this->meta = 0; $this->meta = 0;
$this->getLevel()->setBlock($this, $this, true); $this->getLevelNonNull()->setBlock($this, $this, true);
}else{ }else{
++$this->meta; ++$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{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$down = $this->getSide(Vector3::SIDE_DOWN); $down = $this->getSide(Vector3::SIDE_DOWN);
if($down->getId() === self::SUGARCANE_BLOCK){ 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; return true;
}elseif($down->getId() === self::GRASS or $down->getId() === self::DIRT or $down->getId() === self::SAND){ }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); $block2 = $down->getSide(Vector3::SIDE_WEST);
$block3 = $down->getSide(Vector3::SIDE_EAST); $block3 = $down->getSide(Vector3::SIDE_EAST);
if(($block0 instanceof Water) or ($block1 instanceof Water) or ($block2 instanceof Water) or ($block3 instanceof Water)){ 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; return true;
} }

View File

@ -78,13 +78,13 @@ class TNT extends Solid{
* @return void * @return void
*/ */
public function ignite(int $fuse = 80){ 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; $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 = 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); $nbt->setShort("Fuse", $fuse);
$tnt = Entity::createEntity("PrimedTNT", $this->getLevel(), $nbt); $tnt = Entity::createEntity("PrimedTNT", $this->getLevelNonNull(), $nbt);
if($tnt !== null){ if($tnt !== null){
$tnt->spawnToAll(); $tnt->spawnToAll();

View File

@ -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{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
$down = $this->getSide(Vector3::SIDE_DOWN)->getId(); $down = $this->getSide(Vector3::SIDE_DOWN)->getId();
if($down === self::GRASS or $down === self::DIRT){ if($down === self::GRASS or $down === self::DIRT){
$this->getLevel()->setBlock($blockReplace, $this, true); $this->getLevelNonNull()->setBlock($blockReplace, $this, true);
return true; return true;
} }
@ -63,7 +63,7 @@ class TallGrass extends Flowable{
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){ //Replace with common break method 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);
} }
} }

View File

@ -57,7 +57,7 @@ class Torch extends Flowable{
$face = $faces[$meta] ?? Vector3::SIDE_DOWN; $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))){ 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 Vector3::SIDE_EAST => 1
]; ];
$this->meta = $faces[$face]; $this->meta = $faces[$face];
$this->getLevel()->setBlock($blockReplace, $this, true, true); $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true; return true;
}elseif(!$below->isTransparent() or $below->getId() === self::FENCE or $below->getId() === self::COBBLESTONE_WALL){ }elseif(!$below->isTransparent() or $below->getId() === self::FENCE or $below->getId() === self::COBBLESTONE_WALL){
$this->meta = 0; $this->meta = 0;
$this->getLevel()->setBlock($blockReplace, $this, true, true); $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true; return true;
} }

View File

@ -136,7 +136,7 @@ class Trapdoor extends Transparent{
if(($clickVector->y > 0.5 and $face !== self::SIDE_UP) or $face === self::SIDE_DOWN){ 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->meta |= self::MASK_UPPER; //top half of block
} }
$this->getLevel()->setBlock($blockReplace, $this, true, true); $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true; return true;
} }
@ -146,7 +146,7 @@ class Trapdoor extends Transparent{
public function onActivate(Item $item, Player $player = null) : bool{ public function onActivate(Item $item, Player $player = null) : bool{
$this->meta ^= self::MASK_OPENED; $this->meta ^= self::MASK_OPENED;
$this->getLevel()->setBlock($this, $this, true); $this->getLevelNonNull()->setBlock($this, $this, true);
$this->level->addSound(new DoorSound($this)); $this->level->addSound(new DoorSound($this));
return true; return true;
} }

View File

@ -150,7 +150,7 @@ class Vine extends Flowable{
$this->meta |= $blockReplace->meta; $this->meta |= $blockReplace->meta;
} }
$this->getLevel()->setBlock($blockReplace, $this, true, true); $this->getLevelNonNull()->setBlock($blockReplace, $this, true, true);
return true; return true;
} }

View File

@ -33,7 +33,7 @@ class WallBanner extends StandingBanner{
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if($this->getSide($this->meta ^ 0x01)->getId() === self::AIR){ if($this->getSide($this->meta ^ 0x01)->getId() === self::AIR){
$this->getLevel()->useBreakOn($this); $this->getLevelNonNull()->useBreakOn($this);
} }
} }
} }

View File

@ -33,7 +33,7 @@ class WallSign extends SignPost{
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if($this->getSide($this->meta ^ 0x01)->getId() === self::AIR){ if($this->getSide($this->meta ^ 0x01)->getId() === self::AIR){
$this->getLevel()->useBreakOn($this); $this->getLevelNonNull()->useBreakOn($this);
} }
} }
} }

View File

@ -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{ 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); $ret = $this->getLevelNonNull()->setBlock($this, $this, true, false);
$this->getLevel()->scheduleDelayedBlockUpdate($this, $this->tickRate()); $this->getLevelNonNull()->scheduleDelayedBlockUpdate($this, $this->tickRate());
return $ret; return $ret;
} }

View File

@ -59,7 +59,7 @@ class WaterLily extends Flowable{
if($blockClicked instanceof Water){ if($blockClicked instanceof Water){
$up = $blockClicked->getSide(Vector3::SIDE_UP); $up = $blockClicked->getSide(Vector3::SIDE_UP);
if($up->getId() === Block::AIR){ if($up->getId() === Block::AIR){
$this->getLevel()->setBlock($up, $this, true, true); $this->getLevelNonNull()->setBlock($up, $this, true, true);
return true; return true;
} }
} }
@ -69,7 +69,7 @@ class WaterLily extends Flowable{
public function onNearbyBlockChange() : void{ public function onNearbyBlockChange() : void{
if(!($this->getSide(Vector3::SIDE_DOWN) instanceof Water)){ if(!($this->getSide(Vector3::SIDE_DOWN) instanceof Water)){
$this->getLevel()->useBreakOn($this); $this->getLevelNonNull()->useBreakOn($this);
} }
} }

View File

@ -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{ 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->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{ public function getVariantBitmask() : int{

View File

@ -68,7 +68,7 @@ class CommandReader extends Thread{
$opts = getopt("", ["disable-readline", "enable-readline"]); $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; $this->type = self::TYPE_READLINE;
} }
} }

View File

@ -62,7 +62,7 @@ class GiveCommand extends VanillaCommand{
} }
try{ try{
$item = ItemFactory::fromString($args[1]); $item = ItemFactory::fromStringSingle($args[1]);
}catch(\InvalidArgumentException $e){ }catch(\InvalidArgumentException $e){
$sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.give.item.notFound", [$args[1]])); $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.give.item.notFound", [$args[1]]));
return true; return true;

View File

@ -92,7 +92,7 @@ class ParticleCommand extends VanillaCommand{
} }
if($sender instanceof Player){ if($sender instanceof Player){
$level = $sender->getLevel(); $level = $sender->getLevelNonNull();
$pos = new Vector3( $pos = new Vector3(
$this->getRelativeDouble($sender->getX(), $sender, $args[1]), $this->getRelativeDouble($sender->getX(), $sender, $args[1]),
$this->getRelativeDouble($sender->getY(), $sender, $args[2], 0, Level::Y_MAX), $this->getRelativeDouble($sender->getY(), $sender, $args[2], 0, Level::Y_MAX),

View File

@ -44,7 +44,7 @@ class SeedCommand extends VanillaCommand{
} }
if($sender instanceof Player){ if($sender instanceof Player){
$seed = $sender->getLevel()->getSeed(); $seed = $sender->getLevelNonNull()->getSeed();
}else{ }else{
$seed = $sender->getServer()->getDefaultLevel()->getSeed(); $seed = $sender->getServer()->getDefaultLevel()->getSeed();
} }

View File

@ -51,7 +51,7 @@ class SetWorldSpawnCommand extends VanillaCommand{
if(count($args) === 0){ if(count($args) === 0){
if($sender instanceof Player){ if($sender instanceof Player){
$level = $sender->getLevel(); $level = $sender->getLevelNonNull();
$pos = (new Vector3($sender->x, $sender->y, $sender->z))->round(); $pos = (new Vector3($sender->x, $sender->y, $sender->z))->round();
}else{ }else{
$sender->sendMessage(TextFormat::RED . "You can only perform this command as a player"); $sender->sendMessage(TextFormat::RED . "You can only perform this command as a player");

View File

@ -71,7 +71,7 @@ class SpawnpointCommand extends VanillaCommand{
if(count($args) === 4){ if(count($args) === 4){
if($target->isValid()){ if($target->isValid()){
$level = $target->getLevel(); $level = $target->getLevelNonNull();
$pos = $sender instanceof Player ? $sender->getPosition() : $level->getSpawnLocation(); $pos = $sender instanceof Player ? $sender->getPosition() : $level->getSpawnLocation();
$x = $this->getRelativeDouble($pos->x, $sender, $args[1]); $x = $this->getRelativeDouble($pos->x, $sender, $args[1]);
$y = $this->getRelativeDouble($pos->y, $sender, $args[2], 0, Level::Y_MAX); $y = $this->getRelativeDouble($pos->y, $sender, $args[2], 0, Level::Y_MAX);
@ -84,7 +84,7 @@ class SpawnpointCommand extends VanillaCommand{
} }
}elseif(count($args) <= 1){ }elseif(count($args) <= 1){
if($sender instanceof Player){ 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); $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)])); Command::broadcastCommandMessage($sender, new TranslationContainer("commands.spawnpoint.success", [$target->getName(), round($pos->x, 2), round($pos->y, 2), round($pos->z, 2)]));

View File

@ -24,8 +24,8 @@ declare(strict_types=1);
namespace pocketmine\command\defaults; namespace pocketmine\command\defaults;
use pocketmine\command\CommandSender; use pocketmine\command\CommandSender;
use pocketmine\utils\Process;
use pocketmine\utils\TextFormat; use pocketmine\utils\TextFormat;
use pocketmine\utils\Utils;
use function count; use function count;
use function floor; use function floor;
use function microtime; use function microtime;
@ -48,8 +48,8 @@ class StatusCommand extends VanillaCommand{
return true; return true;
} }
$rUsage = Utils::getRealMemoryUsage(); $rUsage = Process::getRealMemoryUsage();
$mUsage = Utils::getMemoryUsage(true); $mUsage = Process::getAdvancedMemoryUsage();
$server = $sender->getServer(); $server = $sender->getServer();
$sender->sendMessage(TextFormat::GREEN . "---- " . TextFormat::WHITE . "Server status" . TextFormat::GREEN . " ----"); $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 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 . "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 . "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."); $sender->sendMessage(TextFormat::GOLD . "Total memory: " . TextFormat::RED . number_format(round(($mUsage[1] / 1024) / 1024, 2), 2) . " MB.");

View File

@ -77,7 +77,7 @@ class TimeCommand extends VanillaCommand{
return true; return true;
} }
if($sender instanceof Player){ if($sender instanceof Player){
$level = $sender->getLevel(); $level = $sender->getLevelNonNull();
}else{ }else{
$level = $sender->getServer()->getDefaultLevel(); $level = $sender->getServer()->getDefaultLevel();
} }
@ -96,12 +96,28 @@ class TimeCommand extends VanillaCommand{
return true; return true;
} }
if($args[1] === "day"){ switch($args[1]){
$value = Level::TIME_DAY; case "day":
}elseif($args[1] === "night"){ $value = Level::TIME_DAY;
$value = Level::TIME_NIGHT; break;
}else{ case "noon":
$value = $this->getInteger($sender, $args[1], 0); $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){ foreach($sender->getServer()->getLevels() as $level){

View File

@ -68,21 +68,21 @@ class TitleCommand extends VanillaCommand{
throw new InvalidCommandSyntaxException(); throw new InvalidCommandSyntaxException();
} }
$player->addTitle(implode(" ", array_slice($args, 2))); $player->sendTitle(implode(" ", array_slice($args, 2)));
break; break;
case "subtitle": case "subtitle":
if(count($args) < 3){ if(count($args) < 3){
throw new InvalidCommandSyntaxException(); throw new InvalidCommandSyntaxException();
} }
$player->addSubTitle(implode(" ", array_slice($args, 2))); $player->sendSubTitle(implode(" ", array_slice($args, 2)));
break; break;
case "actionbar": case "actionbar":
if(count($args) < 3){ if(count($args) < 3){
throw new InvalidCommandSyntaxException(); throw new InvalidCommandSyntaxException();
} }
$player->addActionBarMessage(implode(" ", array_slice($args, 2))); $player->sendActionBarMessage(implode(" ", array_slice($args, 2)));
break; break;
case "times": case "times":
if(count($args) < 5){ if(count($args) < 5){

View File

@ -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($pos instanceof Position and $pos->level !== null and $pos->level !== $this->level){
if(!$this->switchLevel($pos->getLevel())){ if(!$this->switchLevel($pos->getLevelNonNull())){
return false; return false;
} }
} }
@ -1856,7 +1856,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
$pitch = $pitch ?? $pos->pitch; $pitch = $pitch ?? $pos->pitch;
} }
$from = Position::fromObject($this, $this->level); $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 = new EntityTeleportEvent($this, $from, $to);
$ev->call(); $ev->call();
if($ev->isCancelled()){ if($ev->isCancelled()){
@ -1921,7 +1921,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
protected function sendSpawnPacket(Player $player) : void{ protected function sendSpawnPacket(Player $player) : void{
$pk = new AddActorPacket(); $pk = new AddActorPacket();
$pk->entityRuntimeId = $this->getId(); $pk->entityRuntimeId = $this->getId();
$pk->type = static::NETWORK_ID; $pk->type = AddActorPacket::LEGACY_ID_MAP_BC[static::NETWORK_ID];
$pk->position = $this->asVector3(); $pk->position = $this->asVector3();
$pk->motion = $this->getMotion(); $pk->motion = $this->getMotion();
$pk->yaw = $this->yaw; $pk->yaw = $this->yaw;

View File

@ -191,7 +191,7 @@ abstract class Living extends Entity implements Damageable{
public function hasLineOfSight(Entity $entity) : bool{ public function hasLineOfSight(Entity $entity) : bool{
//TODO: head height //TODO: head height
return true; 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{ protected function onDeath() : void{
$ev = new EntityDeathEvent($this, $this->getDrops()); $ev = new EntityDeathEvent($this, $this->getDrops(), $this->getXpDropAmount());
$ev->call(); $ev->call();
foreach($ev->getDrops() as $item){ 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: 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, $ev->getXpDropAmount());
$this->level->dropExperience($this, $this->getXpDropAmount());
} }
protected function onDeathUpdate(int $tickDiff) : bool{ protected function onDeathUpdate(int $tickDiff) : bool{

View File

@ -98,7 +98,7 @@ class FallingBlock extends Entity{
$hasUpdate = parent::entityBaseTick($tickDiff); $hasUpdate = parent::entityBaseTick($tickDiff);
if(!$this->isFlaggedForDespawn()){ 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); $this->block->position($pos);
@ -113,12 +113,12 @@ class FallingBlock extends Entity{
$block = $this->level->getBlock($pos); $block = $this->level->getBlock($pos);
if(!$block->canBeReplaced() or ($this->onGround and abs($this->y - $this->getFloorY()) > 0.001)){ if(!$block->canBeReplaced() or ($this->onGround and abs($this->y - $this->getFloorY()) > 0.001)){
//FIXME: anvils are supposed to destroy torches //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{ }else{
$ev = new EntityBlockChangeEvent($this, $block, $blockTarget ?? $this->block); $ev = new EntityBlockChangeEvent($this, $block, $blockTarget ?? $this->block);
$ev->call(); $ev->call();
if(!$ev->isCancelled()){ if(!$ev->isCancelled()){
$this->getLevel()->setBlock($pos, $ev->getTo(), true); $this->getLevelNonNull()->setBlock($pos, $ev->getTo(), true);
} }
} }
$hasUpdate = true; $hasUpdate = true;

View File

@ -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. * Returns the entity which caused the damage, or null if the entity has been killed or closed.
*/ */
public function getChild() : ?Entity{ public function getChild() : ?Entity{
return $this->getEntity()->getLevel()->getServer()->findEntity($this->childEntityEid); return $this->getEntity()->getLevelNonNull()->getServer()->findEntity($this->childEntityEid);
} }
} }

View File

@ -62,7 +62,7 @@ class EntityDamageByEntityEvent extends EntityDamageEvent{
* Returns the attacking entity, or null if the attacker has been killed or closed. * Returns the attacking entity, or null if the attacker has been killed or closed.
*/ */
public function getDamager() : ?Entity{ public function getDamager() : ?Entity{
return $this->getEntity()->getLevel()->getServer()->findEntity($this->damagerEntityId); return $this->getEntity()->getLevelNonNull()->getServer()->findEntity($this->damagerEntityId);
} }
public function getKnockBack() : float{ public function getKnockBack() : float{

View File

@ -29,13 +29,16 @@ use pocketmine\item\Item;
class EntityDeathEvent extends EntityEvent{ class EntityDeathEvent extends EntityEvent{
/** @var Item[] */ /** @var Item[] */
private $drops = []; private $drops = [];
/** @var int */
private $xp;
/** /**
* @param Item[] $drops * @param Item[] $drops
*/ */
public function __construct(Living $entity, array $drops = []){ public function __construct(Living $entity, array $drops = [], int $xp = 0){
$this->entity = $entity; $this->entity = $entity;
$this->drops = $drops; $this->drops = $drops;
$this->xp = $xp;
} }
/** /**
@ -58,4 +61,21 @@ class EntityDeathEvent extends EntityEvent{
public function setDrops(array $drops) : void{ public function setDrops(array $drops) : void{
$this->drops = $drops; $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;
}
} }

View File

@ -42,26 +42,44 @@ class EntityDespawnEvent extends EntityEvent{
$this->entityType = $entity::NETWORK_ID; $this->entityType = $entity::NETWORK_ID;
} }
/**
* @deprecated
*/
public function getType() : int{ public function getType() : int{
return $this->entityType; return $this->entityType;
} }
/**
* @deprecated
*/
public function isCreature() : bool{ public function isCreature() : bool{
return $this->entity instanceof Creature; return $this->entity instanceof Creature;
} }
/**
* @deprecated
*/
public function isHuman() : bool{ public function isHuman() : bool{
return $this->entity instanceof Human; return $this->entity instanceof Human;
} }
/**
* @deprecated
*/
public function isProjectile() : bool{ public function isProjectile() : bool{
return $this->entity instanceof Projectile; return $this->entity instanceof Projectile;
} }
/**
* @deprecated
*/
public function isVehicle() : bool{ public function isVehicle() : bool{
return $this->entity instanceof Vehicle; return $this->entity instanceof Vehicle;
} }
/**
* @deprecated
*/
public function isItem() : bool{ public function isItem() : bool{
return $this->entity instanceof ItemEntity; return $this->entity instanceof ItemEntity;
} }

View File

@ -43,30 +43,51 @@ class EntitySpawnEvent extends EntityEvent{
$this->entityType = $entity::NETWORK_ID; $this->entityType = $entity::NETWORK_ID;
} }
/**
* @deprecated
*/
public function getPosition() : Position{ public function getPosition() : Position{
return $this->entity->getPosition(); return $this->entity->getPosition();
} }
/**
* @deprecated
*/
public function getType() : int{ public function getType() : int{
return $this->entityType; return $this->entityType;
} }
/**
* @deprecated
*/
public function isCreature() : bool{ public function isCreature() : bool{
return $this->entity instanceof Creature; return $this->entity instanceof Creature;
} }
/**
* @deprecated
*/
public function isHuman() : bool{ public function isHuman() : bool{
return $this->entity instanceof Human; return $this->entity instanceof Human;
} }
/**
* @deprecated
*/
public function isProjectile() : bool{ public function isProjectile() : bool{
return $this->entity instanceof Projectile; return $this->entity instanceof Projectile;
} }
/**
* @deprecated
*/
public function isVehicle() : bool{ public function isVehicle() : bool{
return $this->entity instanceof Vehicle; return $this->entity instanceof Vehicle;
} }
/**
* @deprecated
*/
public function isItem() : bool{ public function isItem() : bool{
return $this->entity instanceof ItemEntity; return $this->entity instanceof ItemEntity;
} }

View File

@ -47,8 +47,8 @@ class PlayerDeathEvent extends EntityDeathEvent{
* @param Item[] $drops * @param Item[] $drops
* @param string|TextContainer|null $deathMessage Null will cause the default vanilla message to be used * @param string|TextContainer|null $deathMessage Null will cause the default vanilla message to be used
*/ */
public function __construct(Player $entity, array $drops, $deathMessage = null){ public function __construct(Player $entity, array $drops, $deathMessage = null, int $xp = 0){
parent::__construct($entity, $drops); parent::__construct($entity, $drops, $xp);
$this->deathMessage = $deathMessage ?? self::deriveMessage($entity->getDisplayName(), $entity->getLastDamageCause()); $this->deathMessage = $deathMessage ?? self::deriveMessage($entity->getDisplayName(), $entity->getLastDamageCause());
} }

View File

@ -23,7 +23,7 @@ declare(strict_types=1);
namespace pocketmine\event\server; 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 * 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 * Amount of memory already freed
*/ */
public function getMemoryFreed() : int{ 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]);
} }
} }

View File

@ -74,7 +74,7 @@ class ChestInventory extends ContainerInventory{
if(count($this->getViewers()) === 1 and $this->getHolder()->isValid()){ if(count($this->getViewers()) === 1 and $this->getHolder()->isValid()){
//TODO: this crap really shouldn't be managed by the inventory //TODO: this crap really shouldn't be managed by the inventory
$this->broadcastBlockEventPacket(true); $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()){ if(count($this->getViewers()) === 1 and $this->getHolder()->isValid()){
//TODO: this crap really shouldn't be managed by the inventory //TODO: this crap really shouldn't be managed by the inventory
$this->broadcastBlockEventPacket(false); $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); parent::onClose($who);
} }
@ -96,6 +96,6 @@ class ChestInventory extends ContainerInventory{
$pk->z = (int) $holder->z; $pk->z = (int) $holder->z;
$pk->eventType = 1; //it's always 1 for a chest $pk->eventType = 1; //it's always 1 for a chest
$pk->eventData = $isOpen ? 1 : 0; $pk->eventData = $isOpen ? 1 : 0;
$holder->getLevel()->broadcastPacketToViewers($holder, $pk); $holder->getLevelNonNull()->broadcastPacketToViewers($holder, $pk);
} }
} }

View File

@ -60,7 +60,7 @@ class CraftingManager{
if($recipe["block"] !== "crafting_table"){ //TODO: filter others out for now to avoid breaking economics if($recipe["block"] !== "crafting_table"){ //TODO: filter others out for now to avoid breaking economics
break; break;
} }
$this->registerRecipe(new ShapelessRecipe( $this->registerShapelessRecipe(new ShapelessRecipe(
array_map($itemDeserializerFunc, $recipe["input"]), array_map($itemDeserializerFunc, $recipe["input"]),
array_map($itemDeserializerFunc, $recipe["output"]) 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 if($recipe["block"] !== "crafting_table"){ //TODO: filter others out for now to avoid breaking economics
break; break;
} }
$this->registerRecipe(new ShapedRecipe( $this->registerShapedRecipe(new ShapedRecipe(
$recipe["shape"], $recipe["shape"],
array_map($itemDeserializerFunc, $recipe["input"]), array_map($itemDeserializerFunc, $recipe["input"]),
array_map($itemDeserializerFunc, $recipe["output"]) 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 if($recipe["block"] !== "furnace"){ //TODO: filter others out for now to avoid breaking economics
break; break;
} }
$this->registerRecipe(new FurnaceRecipe( $this->registerFurnaceRecipe(new FurnaceRecipe(
Item::jsonDeserialize($recipe["output"]), Item::jsonDeserialize($recipe["output"]),
Item::jsonDeserialize($recipe["input"])) Item::jsonDeserialize($recipe["input"]))
); );
@ -281,6 +281,9 @@ class CraftingManager{
return $this->furnaceRecipes[$input->getId() . ":" . $input->getDamage()] ?? $this->furnaceRecipes[$input->getId() . ":?"] ?? null; return $this->furnaceRecipes[$input->getId() . ":" . $input->getDamage()] ?? $this->furnaceRecipes[$input->getId() . ":?"] ?? null;
} }
/**
* @deprecated
*/
public function registerRecipe(Recipe $recipe) : void{ public function registerRecipe(Recipe $recipe) : void{
$recipe->registerToCraftingManager($this); $recipe->registerToCraftingManager($this);
} }

View File

@ -56,7 +56,7 @@ class EnderChestInventory extends ChestInventory{
*/ */
public function setHolderPosition(EnderChest $enderChest){ public function setHolderPosition(EnderChest $enderChest){
$this->holder->setComponents($enderChest->getFloorX(), $enderChest->getFloorY(), $enderChest->getFloorZ()); $this->holder->setComponents($enderChest->getFloorX(), $enderChest->getFloorY(), $enderChest->getFloorZ());
$this->holder->setLevel($enderChest->getLevel()); $this->holder->setLevel($enderChest->getLevelNonNull());
} }
protected function getOpenSound() : int{ protected function getOpenSound() : int{

View File

@ -53,6 +53,9 @@ class FurnaceRecipe implements Recipe{
return clone $this->output; return clone $this->output;
} }
/**
* @deprecated
*/
public function registerToCraftingManager(CraftingManager $manager) : void{ public function registerToCraftingManager(CraftingManager $manager) : void{
$manager->registerFurnaceRecipe($this); $manager->registerFurnaceRecipe($this);
} }

View File

@ -173,7 +173,7 @@ class PlayerInventory extends BaseInventory{
$this->sendSlot($this->getHeldItemIndex(), $target); $this->sendSlot($this->getHeldItemIndex(), $target);
} }
}else{ }else{
$this->getHolder()->getLevel()->getServer()->broadcastPacket($target, $pk); $this->getHolder()->getLevelNonNull()->getServer()->broadcastPacket($target, $pk);
if(in_array($this->getHolder(), $target, true)){ if(in_array($this->getHolder(), $target, true)){
$this->sendSlot($this->getHeldItemIndex(), $this->getHolder()); $this->sendSlot($this->getHeldItemIndex(), $this->getHolder());
} }

View File

@ -23,7 +23,13 @@ declare(strict_types=1);
namespace pocketmine\inventory; namespace pocketmine\inventory;
/**
* @deprecated
*/
interface Recipe{ interface Recipe{
/**
* @deprecated
*/
public function registerToCraftingManager(CraftingManager $manager) : void; public function registerToCraftingManager(CraftingManager $manager) : void;
} }

View File

@ -176,6 +176,9 @@ class ShapedRecipe implements CraftingRecipe{
return $this->shape; return $this->shape;
} }
/**
* @deprecated
*/
public function registerToCraftingManager(CraftingManager $manager) : void{ public function registerToCraftingManager(CraftingManager $manager) : void{
$manager->registerShapedRecipe($this); $manager->registerShapedRecipe($this);
} }

View File

@ -105,6 +105,9 @@ class ShapelessRecipe implements CraftingRecipe{
return $count; return $count;
} }
/**
* @deprecated
*/
public function registerToCraftingManager(CraftingManager $manager) : void{ public function registerToCraftingManager(CraftingManager $manager) : void{
$manager->registerShapelessRecipe($this); $manager->registerShapelessRecipe($this);
} }

View File

@ -64,7 +64,7 @@ class Bow extends Tool{
$p = $diff / 20; $p = $diff / 20;
$baseForce = min((($p ** 2) + $p * 2) / 3, 1); $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){ if($entity instanceof Projectile){
$infinity = $this->hasEnchantment(Enchantment::INFINITY); $infinity = $this->hasEnchantment(Enchantment::INFINITY);
if($entity instanceof ArrowEntity){ if($entity instanceof ArrowEntity){
@ -110,7 +110,7 @@ class Bow extends Tool{
$ev->getProjectile()->flagForDespawn(); $ev->getProjectile()->flagForDespawn();
}else{ }else{
$ev->getProjectile()->spawnToAll(); $ev->getProjectile()->spawnToAll();
$player->getLevel()->broadcastLevelSoundEvent($player, LevelSoundEventPacket::SOUND_BOW); $player->getLevelNonNull()->broadcastLevelSoundEvent($player, LevelSoundEventPacket::SOUND_BOW);
} }
}else{ }else{
$entity->spawnToAll(); $entity->spawnToAll();

View File

@ -62,8 +62,8 @@ class Bucket extends Item implements MaybeConsumable{
$ev = new PlayerBucketFillEvent($player, $blockReplace, $face, $this, $resultItem); $ev = new PlayerBucketFillEvent($player, $blockReplace, $face, $this, $resultItem);
$ev->call(); $ev->call();
if(!$ev->isCancelled()){ if(!$ev->isCancelled()){
$player->getLevel()->setBlock($blockClicked, BlockFactory::get(Block::AIR), true, true); $player->getLevelNonNull()->setBlock($blockClicked, BlockFactory::get(Block::AIR), true, true);
$player->getLevel()->broadcastLevelSoundEvent($blockClicked->add(0.5, 0.5, 0.5), $blockClicked->getBucketFillSound()); $player->getLevelNonNull()->broadcastLevelSoundEvent($blockClicked->add(0.5, 0.5, 0.5), $blockClicked->getBucketFillSound());
if($player->isSurvival()){ if($player->isSurvival()){
if($stack->getCount() === 0){ if($stack->getCount() === 0){
$player->getInventory()->setItemInHand($ev->getItem()); $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 = new PlayerBucketEmptyEvent($player, $blockReplace, $face, $this, ItemFactory::get(Item::BUCKET));
$ev->call(); $ev->call();
if(!$ev->isCancelled()){ if(!$ev->isCancelled()){
$player->getLevel()->setBlock($blockReplace, $resultBlock->getFlowingForm(), true, true); $player->getLevelNonNull()->setBlock($blockReplace, $resultBlock->getFlowingForm(), true, true);
$player->getLevel()->broadcastLevelSoundEvent($blockReplace->add(0.5, 0.5, 0.5), $resultBlock->getBucketEmptySound()); $player->getLevelNonNull()->broadcastLevelSoundEvent($blockReplace->add(0.5, 0.5, 0.5), $resultBlock->getBucketEmptySound());
if($player->isSurvival()){ if($player->isSurvival()){
$player->getInventory()->setItemInHand($ev->getItem()); $player->getInventory()->setItemInHand($ev->getItem());

View File

@ -50,11 +50,10 @@ class ChorusFruit extends Food{
} }
public function onConsume(Living $consumer){ public function onConsume(Living $consumer){
$level = $consumer->getLevel(); $level = $consumer->getLevelNonNull();
assert($level !== null);
$minX = $consumer->getFloorX() - 8; $minX = $consumer->getFloorX() - 8;
$minY = min($consumer->getFloorY(), $consumer->getLevel()->getWorldHeight()) - 8; $minY = min($consumer->getFloorY(), $consumer->getLevelNonNull()->getWorldHeight()) - 8;
$minZ = $consumer->getFloorZ() - 8; $minZ = $consumer->getFloorZ() - 8;
$maxX = $minX + 16; $maxX = $minX + 16;

View File

@ -28,7 +28,6 @@ use pocketmine\block\BlockFactory;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket; use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
use pocketmine\Player; use pocketmine\Player;
use function assert;
class FlintSteel extends Tool{ class FlintSteel extends Tool{
public function __construct(int $meta = 0){ 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{ public function onActivate(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : bool{
if($blockReplace->getId() === self::AIR){ if($blockReplace->getId() === self::AIR){
$level = $player->getLevel(); $level = $player->getLevelNonNull();
assert($level !== null);
$level->setBlock($blockReplace, BlockFactory::get(Block::FIRE), true); $level->setBlock($blockReplace, BlockFactory::get(Block::FIRE), true);
$level->broadcastLevelSoundEvent($blockReplace->add(0.5, 0.5, 0.5), LevelSoundEventPacket::SOUND_IGNITE); $level->broadcastLevelSoundEvent($blockReplace->add(0.5, 0.5, 0.5), LevelSoundEventPacket::SOUND_IGNITE);

View File

@ -852,7 +852,7 @@ class Item implements ItemIds, \JsonSerializable{
$item = ItemFactory::get($idTag->getValue(), $meta, $count); $item = ItemFactory::get($idTag->getValue(), $meta, $count);
}elseif($idTag instanceof StringTag){ //PC item save format }elseif($idTag instanceof StringTag){ //PC item save format
try{ try{
$item = ItemFactory::fromString($idTag->getValue()); $item = ItemFactory::fromStringSingle($idTag->getValue());
}catch(\InvalidArgumentException $e){ }catch(\InvalidArgumentException $e){
//TODO: improve error handling //TODO: improve error handling
return ItemFactory::get(Item::AIR, 0, 0); return ItemFactory::get(Item::AIR, 0, 0);

Some files were not shown because too many files have changed in this diff Show More