Merge branch 'minor-next' into major-next

This commit is contained in:
Dylan K. Taylor 2023-08-09 16:35:32 +01:00
commit 32d67080e5
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
21 changed files with 360 additions and 39 deletions

View File

@ -49,3 +49,14 @@ Released 1st August 2023.
## Fixes
- Fixed exponentially increasing lag when many hundreds of non-mergeable dropped items occupied the same space. This disproportionately affected SkyBlock servers due to large cactus farms using water to collect items together.
# 4.23.5
Released 9th August 2023.
## General
- Updated translation data to [pmmp/Language 2.19.6](https://github.com/pmmp/Language/releases/tag/2.19.6).
## Fixes
- Fixed `PluginBase->saveResource()` leaking file resources when the data file already exists in the plugin's data folder. This bug existed since 2014 and was only discovered recently.
- Fixed coral blocks becoming dead after calling `getDropsForCompatibleTool()` on them.
- Fixed `BlockDeathEvent->getOldState()` returning a block which is already dead.

View File

@ -81,3 +81,29 @@ Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if
- In addition, this change facilitates the use of the newly introduced `Block->getAdjacentSupportType()` API method, reducing boilerplate support-type checking code.
- Bell block code has been simplified and cleaned up.
- `TallGrass` and `DoubleTallGrass` now use a shared `TallGrassTrait` to reduce code duplication.
# 5.4.1
Released 8th August 2023.
## General
- Updated translation data to [pmmp/Language 2.19.6](https://github.com/pmmp/Language/releases/tag/2.19.6).
- [`ext-pmmpthread` 6.0.7](https://github.com/pmmp/ext-pmmpthread/releases/tag/6.0.7) is now required (needed for bug fixes).
## Fixes
- Fixed Podzol not dropping itself when mined with Silk Touch.
- Fixed `World->getSafeSpawn()` not accepting positions below `y=0` (world height limit change).
- Fixed `pocketmine\thread\Thread` and `pocketmine\thread\Worker` instances not logging any information when they crash.
- Fixed `CraftItemEvent` not cloning returned items.
## Internals
- Foreach by-reference is now disallowed via a custom PHPStan rule.
# 5.4.2
Released 9th August 2023.
## Included releases
- [4.23.5](https://github.com/pmmp/PocketMine-MP/blob/4.23.5/changelogs/4.23.md#4235) - Minor bug fixes
## Fixes
- Fixed cake accepting candle placement when slices have already been eaten.
- Fixed fire charges not lighting candles.

View File

@ -31,7 +31,7 @@ use function str_repeat;
final class VersionInfo{
public const NAME = "PocketMine-MP";
public const BASE_VERSION = "5.4.1";
public const BASE_VERSION = "5.4.3";
public const IS_DEVELOPMENT_BUILD = true;
public const BUILD_CHANNEL = "stable";

View File

@ -736,8 +736,9 @@ final class BlockTypeIds{
public const SMALL_DRIPLEAF = 10706;
public const BIG_DRIPLEAF_HEAD = 10707;
public const BIG_DRIPLEAF_STEM = 10708;
public const PINK_PETALS = 10709;
public const FIRST_UNUSED_BLOCK_ID = 10709;
public const FIRST_UNUSED_BLOCK_ID = 10710;
private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID;

View File

@ -64,7 +64,7 @@ class Cake extends BaseCake{
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($item instanceof ItemBlock){
if($this->bites === 0 && $item instanceof ItemBlock){
$block = $item->getBlock();
$resultBlock = null;
if($block->getTypeId() === BlockTypeIds::CANDLE){

View File

@ -61,7 +61,7 @@ final class CoralBlock extends Opaque{
}
public function getDropsForCompatibleTool(Item $item) : array{
return [$this->setDead(true)->asItem()];
return [(clone $this)->setDead(true)->asItem()];
}
public function isAffectedBySilkTouch() : bool{

View File

@ -26,6 +26,7 @@ namespace pocketmine\block;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\entity\Entity;
use pocketmine\entity\Living;
use pocketmine\event\block\FarmlandHydrationChangeEvent;
use pocketmine\event\entity\EntityTrampleFarmlandEvent;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
@ -73,14 +74,22 @@ class Farmland extends Transparent{
$world = $this->position->getWorld();
if(!$this->canHydrate()){
if($this->wetness > 0){
$this->wetness--;
$world->setBlock($this->position, $this, false);
$event = new FarmlandHydrationChangeEvent($this, $this->wetness, $this->wetness - 1);
$event->call();
if(!$event->isCancelled()){
$this->wetness = $event->getNewHydration();
$world->setBlock($this->position, $this, false);
}
}else{
$world->setBlock($this->position, VanillaBlocks::DIRT());
}
}elseif($this->wetness < self::MAX_WETNESS){
$this->wetness = self::MAX_WETNESS;
$world->setBlock($this->position, $this, false);
$event = new FarmlandHydrationChangeEvent($this, $this->wetness, self::MAX_WETNESS);
$event->call();
if(!$event->isCancelled()){
$this->wetness = $event->getNewHydration();
$world->setBlock($this->position, $this, false);
}
}
}

View File

@ -145,9 +145,13 @@ class Fire extends BaseFire{
private function burnBlock(Block $block, int $chanceBound) : void{
if(mt_rand(0, $chanceBound) < $block->getFlammability()){
$ev = new BlockBurnEvent($block, $this);
$ev->call();
if(!$ev->isCancelled()){
$cancelled = false;
if(BlockBurnEvent::hasHandlers()){
$ev = new BlockBurnEvent($block, $this);
$ev->call();
$cancelled = $ev->isCancelled();
}
if(!$cancelled){
$block->onIncinerate();
$world = $this->position->getWorld();

View File

@ -116,10 +116,15 @@ class Leaves extends Transparent{
public function onRandomTick() : void{
if(!$this->noDecay && $this->checkDecay){
$ev = new LeavesDecayEvent($this);
$ev->call();
$cancelled = false;
if(LeavesDecayEvent::hasHandlers()){
$ev = new LeavesDecayEvent($this);
$ev->call();
$cancelled = $ev->isCancelled();
}
$world = $this->position->getWorld();
if($ev->isCancelled() || $this->findLog($this->position)){
if($cancelled || $this->findLog($this->position)){
$this->checkDecay = false;
$world->setBlock($this->position, $this, false);
}else{

119
src/block/PinkPetals.php Normal file
View File

@ -0,0 +1,119 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Fertilizer;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
class PinkPetals extends Flowable{
use HorizontalFacingTrait;
public const MIN_COUNT = 1;
public const MAX_COUNT = 4;
protected int $count = self::MIN_COUNT;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->horizontalFacing($this->facing);
$w->boundedInt(2, self::MIN_COUNT, self::MAX_COUNT, $this->count);
}
public function getCount() : int{
return $this->count;
}
/** @return $this */
public function setCount(int $count) : self{
if($count < self::MIN_COUNT || $count > self::MAX_COUNT){
throw new \InvalidArgumentException("Count must be in range " . self::MIN_COUNT . " ... " . self::MAX_COUNT);
}
$this->count = $count;
return $this;
}
private function canBeSupportedAt(Block $block) : bool{
$supportBlock = $block->getSide(Facing::DOWN);
//TODO: Moss block
return $supportBlock->hasTypeTag(BlockTypeTags::DIRT) || $supportBlock->hasTypeTag(BlockTypeTags::MUD);
}
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedAt($this)){
$this->position->getWorld()->useBreakOn($this->position);
}
}
public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
return ($blockReplace instanceof PinkPetals && $blockReplace->getCount() < self::MAX_COUNT) || parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$this->canBeSupportedAt($this)){
return false;
}
if($blockReplace instanceof PinkPetals && $blockReplace->getCount() < self::MAX_COUNT){
$this->count = $blockReplace->getCount() + 1;
$this->facing = $blockReplace->getFacing();
}elseif($player !== null){
$this->facing = Facing::opposite($player->getHorizontalFacing());
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($item instanceof Fertilizer){
$grew = false;
if($this->count < self::MAX_COUNT){
$grew = BlockEventHelper::grow($this, (clone $this)->setCount($this->count + 1), $player);
}else{
$this->position->getWorld()->dropItem($this->position->add(0, 0.5, 0), $this->asItem());
$grew = true;
}
if($grew){
$item->pop();
return true;
}
}
return false;
}
public function getFlameEncouragement() : int{
return 60;
}
public function getFlammability() : int{
return 100;
}
public function getDropsForCompatibleTool(Item $item) : array{
return [$this->asItem()->setCount($this->count)];
}
}

View File

@ -573,6 +573,7 @@ use function mb_strtolower;
* @method static PackedIce PACKED_ICE()
* @method static Opaque PACKED_MUD()
* @method static DoublePlant PEONY()
* @method static PinkPetals PINK_PETALS()
* @method static Flower PINK_TULIP()
* @method static Podzol PODZOL()
* @method static Opaque POLISHED_ANDESITE()
@ -840,6 +841,7 @@ final class VanillaBlocks{
self::register("lilac", new DoublePlant(new BID(Ids::LILAC), "Lilac", new Info(BreakInfo::instant())));
self::register("rose_bush", new DoublePlant(new BID(Ids::ROSE_BUSH), "Rose Bush", new Info(BreakInfo::instant())));
self::register("peony", new DoublePlant(new BID(Ids::PEONY), "Peony", new Info(BreakInfo::instant())));
self::register("pink_petals", new PinkPetals(new BID(Ids::PINK_PETALS), "Pink Petals", new Info(BreakInfo::instant())));
self::register("double_tallgrass", new DoubleTallGrass(new BID(Ids::DOUBLE_TALLGRASS), "Double Tallgrass", new Info(BreakInfo::instant(ToolType::SHEARS, 1))));
self::register("large_fern", new DoubleTallGrass(new BID(Ids::LARGE_FERN), "Large Fern", new Info(BreakInfo::instant(ToolType::SHEARS, 1))));
self::register("dragon_egg", new DragonEgg(new BID(Ids::DRAGON_EGG), "Dragon Egg", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD()))));

View File

@ -33,6 +33,7 @@ use pocketmine\item\ItemTypeIds;
use pocketmine\math\RayTraceResult;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\sound\BlazeShootSound;
use pocketmine\world\sound\FireExtinguishSound;
use pocketmine\world\sound\FlintSteelSound;
@ -57,12 +58,16 @@ trait CandleTrait{
/** @see Block::onInteract() */
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($item->getTypeId() === ItemTypeIds::FLINT_AND_STEEL || $item->hasEnchantment(VanillaEnchantments::FIRE_ASPECT())){
if($item->getTypeId() === ItemTypeIds::FIRE_CHARGE || $item->getTypeId() === ItemTypeIds::FLINT_AND_STEEL || $item->hasEnchantment(VanillaEnchantments::FIRE_ASPECT())){
if($this->lit){
return true;
}
if($item instanceof Durable){
$item->applyDamage(1);
}elseif($item->getTypeId() === ItemTypeIds::FIRE_CHARGE){
$item->pop();
//TODO: not sure if this is intentional, but it's what Bedrock currently does as of 1.20.10
$this->position->getWorld()->addSound($this->position, new BlazeShootSound());
}
$this->position->getWorld()->addSound($this->position, new FlintSteelSound());
$this->position->getWorld()->setBlock($this->position, $this->setLit(true));

View File

@ -100,6 +100,7 @@ use pocketmine\block\MobHead;
use pocketmine\block\NetherPortal;
use pocketmine\block\NetherVines;
use pocketmine\block\NetherWartPlant;
use pocketmine\block\PinkPetals;
use pocketmine\block\Potato;
use pocketmine\block\PoweredRail;
use pocketmine\block\PumpkinStem;
@ -1364,6 +1365,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->map(Blocks::ORANGE_TULIP(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_TULIP_ORANGE));
$this->map(Blocks::OXEYE_DAISY(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_OXEYE));
$this->map(Blocks::PEONY(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, StringValues::DOUBLE_PLANT_TYPE_PAEONIA, Writer::create(Ids::DOUBLE_PLANT)));
$this->map(Blocks::PINK_PETALS(), function(PinkPetals $block) : Writer{
return Writer::create(Ids::PINK_PETALS)
->writeLegacyHorizontalFacing($block->getFacing())
->writeInt(StateNames::GROWTH, $block->getCount() - 1);
});
$this->map(Blocks::PINK_TULIP(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_TULIP_PINK));
$this->map(Blocks::POLISHED_ANDESITE(), fn() => Helper::encodeStone(StringValues::STONE_TYPE_ANDESITE_SMOOTH));
$this->map(Blocks::POLISHED_ANDESITE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_POLISHED_ANDESITE));

View File

@ -28,6 +28,7 @@ use pocketmine\block\Block;
use pocketmine\block\CaveVines;
use pocketmine\block\ChorusFlower;
use pocketmine\block\Light;
use pocketmine\block\PinkPetals;
use pocketmine\block\Slab;
use pocketmine\block\Stair;
use pocketmine\block\SweetBerryBush;
@ -1175,6 +1176,13 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->mapSlab(Ids::OXIDIZED_CUT_COPPER_SLAB, Ids::OXIDIZED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::OXIDIZED()));
$this->mapStairs(Ids::OXIDIZED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::OXIDIZED()));
$this->map(Ids::PEARLESCENT_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::PEARLESCENT())->setAxis($in->readPillarAxis()));
$this->map(Ids::PINK_PETALS, function(Reader $in) : Block{
//Pink petals only uses 0-3, but GROWTH state can go up to 7
$growth = $in->readBoundedInt(StateNames::GROWTH, 0, 7);
return Blocks::PINK_PETALS()
->setFacing($in->readLegacyHorizontalFacing())
->setCount(min($growth + 1, PinkPetals::MAX_COUNT));
});
$this->mapStairs(Ids::POLISHED_ANDESITE_STAIRS, fn() => Blocks::POLISHED_ANDESITE_STAIRS());
$this->map(Ids::POLISHED_BASALT, function(Reader $in) : Block{
return Blocks::POLISHED_BASALT()

View File

@ -0,0 +1,59 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\event\block;
use pocketmine\block\Block;
use pocketmine\block\Farmland;
use pocketmine\event\Cancellable;
use pocketmine\event\CancellableTrait;
/**
* Called when farmland hydration is updated.
*/
class FarmlandHydrationChangeEvent extends BlockEvent implements Cancellable{
use CancellableTrait;
public function __construct(
Block $block,
private int $oldHydration,
private int $newHydration,
){
parent::__construct($block);
}
public function getOldHydration() : int{
return $this->oldHydration;
}
public function getNewHydration() : int{
return $this->newHydration;
}
public function setNewHydration(int $hydration) : void{
if($hydration < 0 || $hydration > Farmland::MAX_WETNESS){
throw new \InvalidArgumentException("Hydration must be in range 0 ... " . Farmland::MAX_WETNESS);
}
$this->newHydration = $hydration;
}
}

View File

@ -30,6 +30,7 @@ use pocketmine\event\Event;
use pocketmine\inventory\transaction\CraftingTransaction;
use pocketmine\item\Item;
use pocketmine\player\Player;
use pocketmine\utils\Utils;
class CraftItemEvent extends Event implements Cancellable{
use CancellableTrait;
@ -74,7 +75,7 @@ class CraftItemEvent extends Event implements Cancellable{
* @return Item[]
*/
public function getInputs() : array{
return $this->inputs;
return Utils::cloneObjectArray($this->inputs);
}
/**
@ -83,7 +84,7 @@ class CraftItemEvent extends Event implements Cancellable{
* @return Item[]
*/
public function getOutputs() : array{
return $this->outputs;
return Utils::cloneObjectArray($this->outputs);
}
public function getPlayer() : Player{

View File

@ -0,0 +1,44 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\event\world;
use pocketmine\world\World;
/**
* Called when a world's difficulty is changed.
*/
final class WorldDifficultyChangeEvent extends WorldEvent{
public function __construct(
World $world,
private int $oldDifficulty,
private int $newDifficulty
){
parent::__construct($world);
}
public function getOldDifficulty() : int{ return $this->oldDifficulty; }
public function getNewDifficulty() : int{ return $this->newDifficulty; }
}

View File

@ -856,6 +856,7 @@ final class StringToItemParser extends StringToTParser{
$result->registerBlock("packed_ice", fn() => Blocks::PACKED_ICE());
$result->registerBlock("packed_mud", fn() => Blocks::PACKED_MUD());
$result->registerBlock("peony", fn() => Blocks::PEONY());
$result->registerBlock("pink_petals", fn() => Blocks::PINK_PETALS());
$result->registerBlock("pink_tulip", fn() => Blocks::PINK_TULIP());
$result->registerBlock("piglin_head", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::PIGLIN()));
$result->registerBlock("plank", fn() => Blocks::OAK_PLANKS());

View File

@ -34,9 +34,9 @@ use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Config;
use pocketmine\utils\Utils;
use Symfony\Component\Filesystem\Path;
use function copy;
use function count;
use function dirname;
use function fclose;
use function file_exists;
use function fopen;
use function is_dir;
@ -44,7 +44,6 @@ use function mkdir;
use function rtrim;
use function str_contains;
use function str_replace;
use function stream_copy_to_stream;
use function strlen;
use function strtolower;
use function substr;
@ -215,6 +214,27 @@ abstract class PluginBase implements Plugin, CommandExecutor{
}
/**
* Returns the path to the folder where the plugin's embedded resource files are usually located.
* Note: This is NOT the same as the data folder. The files in this folder should be considered read-only.
*/
public function getResourceFolder() : string{
return $this->resourceFolder;
}
/**
* Returns the full path to a data file in the plugin's resources folder.
* This path can be used with standard PHP functions like fopen() or file_get_contents().
*
* Note: Any path returned by this function should be considered READ-ONLY.
*/
public function getResourcePath(string $filename) : string{
return Path::join($this->getResourceFolder(), $filename);
}
/**
* @deprecated Prefer using standard PHP functions with {@link PluginBase::getResourcePath()}, like
* file_get_contents() or fopen().
*
* Gets an embedded resource on the plugin file.
* WARNING: You must close the resource given using fclose()
*
@ -239,26 +259,21 @@ abstract class PluginBase implements Plugin, CommandExecutor{
return false;
}
if(($resource = $this->getResource($filename)) === null){
$source = Path::join($this->resourceFolder, $filename);
if(!file_exists($source)){
return false;
}
$out = Path::join($this->dataFolder, $filename);
if(!file_exists(dirname($out))){
mkdir(dirname($out), 0755, true);
}
if(file_exists($out) && !$replace){
$destination = Path::join($this->dataFolder, $filename);
if(file_exists($destination) && !$replace){
return false;
}
$fp = fopen($out, "wb");
if($fp === false) throw new AssumptionFailedError("fopen() should not fail with wb flags");
if(!file_exists(dirname($destination))){
mkdir(dirname($destination), 0755, true);
}
$ret = stream_copy_to_stream($resource, $fp) > 0;
fclose($fp);
fclose($resource);
return $ret;
return copy($source, $destination);
}
/**

View File

@ -52,6 +52,7 @@ use pocketmine\event\world\ChunkLoadEvent;
use pocketmine\event\world\ChunkPopulateEvent;
use pocketmine\event\world\ChunkUnloadEvent;
use pocketmine\event\world\SpawnChangeEvent;
use pocketmine\event\world\WorldDifficultyChangeEvent;
use pocketmine\event\world\WorldDisplayNameChangeEvent;
use pocketmine\event\world\WorldParticleEvent;
use pocketmine\event\world\WorldSaveEvent;
@ -971,14 +972,17 @@ class World implements ChunkManager{
$block = $replacement;
}
$ev = new BlockUpdateEvent($block);
$ev->call();
if(!$ev->isCancelled()){
foreach($this->getNearbyEntities(AxisAlignedBB::one()->offset($x, $y, $z)) as $entity){
$entity->onNearbyBlockChange();
if(BlockUpdateEvent::hasHandlers()){
$ev = new BlockUpdateEvent($block);
$ev->call();
if($ev->isCancelled()){
continue;
}
$block->onNearbyBlockChange();
}
foreach($this->getNearbyEntities(AxisAlignedBB::one()->offset($x, $y, $z)) as $entity){
$entity->onNearbyBlockChange();
}
$block->onNearbyBlockChange();
}
$this->timings->scheduledBlockUpdates->stopTiming();
@ -3127,6 +3131,7 @@ class World implements ChunkManager{
if($difficulty < 0 || $difficulty > 3){
throw new \InvalidArgumentException("Invalid difficulty level $difficulty");
}
(new WorldDifficultyChangeEvent($this, $this->getDifficulty(), $difficulty))->call();
$this->provider->getWorldData()->setDifficulty($difficulty);
foreach($this->players as $player){

File diff suppressed because one or more lines are too long