mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-05-12 00:39:45 +00:00
Merge branch 'minor-next' into major-next
This commit is contained in:
commit
32d67080e5
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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){
|
||||
|
@ -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{
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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
119
src/block/PinkPetals.php
Normal 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)];
|
||||
}
|
||||
}
|
@ -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()))));
|
||||
|
@ -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));
|
||||
|
@ -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));
|
||||
|
@ -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()
|
||||
|
59
src/event/block/FarmlandHydrationChangeEvent.php
Normal file
59
src/event/block/FarmlandHydrationChangeEvent.php
Normal 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;
|
||||
}
|
||||
}
|
@ -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{
|
||||
|
44
src/event/world/WorldDifficultyChangeEvent.php
Normal file
44
src/event/world/WorldDifficultyChangeEvent.php
Normal 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; }
|
||||
}
|
@ -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());
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user