From 0629d11e130ba3d9b2b240c97dfbecfa79e121a1 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 16 Apr 2023 20:51:55 +0100 Subject: [PATCH 01/19] Avoid unnecessary events work in handleDataPacket if the events have no registered handlers this particular optimisation became possible thanks to changes in 4.19. I observed that the allocation of Event objects and calling ->call() was costing us a significant percentage of the time taken in PlayerAuthInputPacket handlers. This change produces a measurable 2 microsecond reduction in overhead for PlayerAuthInputPacket handling when players are not moving (10.7 -> 8.7 microseconds). On a server with 200 players, this translates into a 1% reduction in CPU load for PlayerAuthInputPacket alone. It will also benefit other packets, but not to the extent that PlayerAuthInputPacket benefits. --- src/event/Event.php | 11 +++++++++ src/network/mcpe/NetworkSession.php | 35 ++++++++++++++++------------- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/event/Event.php b/src/event/Event.php index 1ae7bb96f6..6264d85259 100644 --- a/src/event/Event.php +++ b/src/event/Event.php @@ -27,6 +27,7 @@ declare(strict_types=1); namespace pocketmine\event; use pocketmine\timings\Timings; +use function count; use function get_class; abstract class Event{ @@ -67,4 +68,14 @@ abstract class Event{ $timings->stopTiming(); } } + + /** + * Returns whether the current class context has any registered global handlers. + * This can be used in hot code paths to avoid unnecessary event object creation. + * + * Usage: SomeEventClass::hasHandlers() + */ + public static function hasHandlers() : bool{ + return count(HandlerListManager::global()->getListFor(static::class)->getListenerList()) > 0; + } } diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 7f07fbd77c..dfba61babc 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -401,10 +401,12 @@ class NetworkSession{ $timings->startTiming(); try{ - $ev = new DataPacketDecodeEvent($this, $packet->pid(), $buffer); - $ev->call(); - if($ev->isCancelled()){ - return; + if(DataPacketDecodeEvent::hasHandlers()){ + $ev = new DataPacketDecodeEvent($this, $packet->pid(), $buffer); + $ev->call(); + if($ev->isCancelled()){ + return; + } } $decodeTimings = Timings::getDecodeDataPacketTimings($packet); @@ -424,19 +426,22 @@ class NetworkSession{ $decodeTimings->stopTiming(); } - $ev = new DataPacketReceiveEvent($this, $packet); - $ev->call(); - if(!$ev->isCancelled()){ - $handlerTimings = Timings::getHandleDataPacketTimings($packet); - $handlerTimings->startTiming(); - try{ - if($this->handler === null || !$packet->handle($this->handler)){ - $this->logger->debug("Unhandled " . $packet->getName() . ": " . base64_encode($stream->getBuffer())); - } - }finally{ - $handlerTimings->stopTiming(); + if(DataPacketReceiveEvent::hasHandlers()){ + $ev = new DataPacketReceiveEvent($this, $packet); + $ev->call(); + if($ev->isCancelled()){ + return; } } + $handlerTimings = Timings::getHandleDataPacketTimings($packet); + $handlerTimings->startTiming(); + try{ + if($this->handler === null || !$packet->handle($this->handler)){ + $this->logger->debug("Unhandled " . $packet->getName() . ": " . base64_encode($stream->getBuffer())); + } + }finally{ + $handlerTimings->stopTiming(); + } }finally{ $timings->stopTiming(); } From 26086372107283aad399fb0613d1c284561398be Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 1 Aug 2023 17:37:49 +0100 Subject: [PATCH 02/19] HandlerListManager: track RegisteredListenerCache directly This change improves the performance of calling an event with 0 handlers by about 10% with no other changes. Since we have to access the list eventually anyway, we can cut out some unnecessary work by returning the handlers from the cache directly, instead of fetching the HandlerList for no reason. This also improves the performance of Event::hasHandlers() by about 40%, which is pretty significant (120 ns -> 80 ns). --- src/event/Event.php | 6 +++--- src/event/HandlerList.php | 6 ++---- src/event/HandlerListManager.php | 25 ++++++++++++++++++++++++- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/event/Event.php b/src/event/Event.php index 49652fcc60..21b8ae36a5 100644 --- a/src/event/Event.php +++ b/src/event/Event.php @@ -55,11 +55,11 @@ abstract class Event{ $timings = Timings::getEventTimings($this); $timings->startTiming(); - $handlerList = HandlerListManager::global()->getListFor(get_class($this)); + $handlers = HandlerListManager::global()->getHandlersFor(static::class); ++self::$eventCallDepth; try{ - foreach($handlerList->getListenerList() as $registration){ + foreach($handlers as $registration){ $registration->callEvent($this); } }finally{ @@ -75,6 +75,6 @@ abstract class Event{ * Usage: SomeEventClass::hasHandlers() */ public static function hasHandlers() : bool{ - return count(HandlerListManager::global()->getListFor(static::class)->getListenerList()) > 0; + return count(HandlerListManager::global()->getHandlersFor(static::class)) > 0; } } diff --git a/src/event/HandlerList.php b/src/event/HandlerList.php index 37811e9592..74eedf3a4a 100644 --- a/src/event/HandlerList.php +++ b/src/event/HandlerList.php @@ -33,8 +33,6 @@ class HandlerList{ /** @var RegisteredListener[][] */ private array $handlerSlots = []; - private RegisteredListenerCache $handlerCache; - /** @var RegisteredListenerCache[] */ private array $affectedHandlerCaches = []; @@ -44,9 +42,9 @@ class HandlerList{ */ public function __construct( private string $class, - private ?HandlerList $parentList + private ?HandlerList $parentList, + private RegisteredListenerCache $handlerCache = new RegisteredListenerCache() ){ - $this->handlerCache = new RegisteredListenerCache(); for($list = $this; $list !== null; $list = $list->parentList){ $list->affectedHandlerCaches[spl_object_id($this->handlerCache)] = $this->handlerCache; } diff --git a/src/event/HandlerListManager.php b/src/event/HandlerListManager.php index ab94674cfa..047632f548 100644 --- a/src/event/HandlerListManager.php +++ b/src/event/HandlerListManager.php @@ -36,6 +36,11 @@ class HandlerListManager{ /** @var HandlerList[] classname => HandlerList */ private array $allLists = []; + /** + * @var RegisteredListenerCache[] event class name => cache + * @phpstan-var array, RegisteredListenerCache> + */ + private array $handlerCaches = []; /** * Unregisters all the listeners @@ -98,7 +103,25 @@ class HandlerListManager{ } $parent = self::resolveNearestHandleableParent($class); - return $this->allLists[$event] = new HandlerList($event, $parent !== null ? $this->getListFor($parent->getName()) : null); + $cache = new RegisteredListenerCache(); + $this->handlerCaches[$event] = $cache; + return $this->allLists[$event] = new HandlerList( + $event, + parentList: $parent !== null ? $this->getListFor($parent->getName()) : null, + handlerCache: $cache + ); + } + + /** + * @phpstan-template TEvent of Event + * @phpstan-param class-string $event + * + * @return RegisteredListener[] + */ + public function getHandlersFor(string $event) : array{ + $cache = $this->handlerCaches[$event] ?? null; + //getListFor() will populate the cache for the next call + return $cache?->list ?? $this->getListFor($event)->getListenerList(); } /** From 0b86fafafbb8630543ff8d6c35645e42f76721e9 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 1 Aug 2023 17:41:53 +0100 Subject: [PATCH 03/19] Hot path optimisation for DataPacketSendEvent --- src/network/mcpe/NetworkSession.php | 14 +++++++++----- src/network/mcpe/StandardPacketBroadcaster.php | 12 +++++++----- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 894b1434bf..100d9c989e 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -464,12 +464,16 @@ class NetworkSession{ $timings = Timings::getSendDataPacketTimings($packet); $timings->startTiming(); try{ - $ev = new DataPacketSendEvent([$this], [$packet]); - $ev->call(); - if($ev->isCancelled()){ - return false; + if(DataPacketSendEvent::hasHandlers()){ + $ev = new DataPacketSendEvent([$this], [$packet]); + $ev->call(); + if($ev->isCancelled()){ + return false; + } + $packets = $ev->getPackets(); + }else{ + $packets = [$packet]; } - $packets = $ev->getPackets(); foreach($packets as $evPacket){ $this->addToSendBuffer(self::encodePacketTimed(PacketSerializer::encoder($this->packetSerializerContext), $evPacket)); diff --git a/src/network/mcpe/StandardPacketBroadcaster.php b/src/network/mcpe/StandardPacketBroadcaster.php index 9de3d214af..c200859fdd 100644 --- a/src/network/mcpe/StandardPacketBroadcaster.php +++ b/src/network/mcpe/StandardPacketBroadcaster.php @@ -44,12 +44,14 @@ final class StandardPacketBroadcaster implements PacketBroadcaster{ public function broadcastPackets(array $recipients, array $packets) : void{ //TODO: this shouldn't really be called here, since the broadcaster might be replaced by an alternative //implementation that doesn't fire events - $ev = new DataPacketSendEvent($recipients, $packets); - $ev->call(); - if($ev->isCancelled()){ - return; + if(DataPacketSendEvent::hasHandlers()){ + $ev = new DataPacketSendEvent($recipients, $packets); + $ev->call(); + if($ev->isCancelled()){ + return; + } + $packets = $ev->getPackets(); } - $packets = $ev->getPackets(); $compressors = []; From 82f87cc2dac70b0dd74a26534f7e04fef88ce3a3 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 2 Aug 2023 13:40:12 +0100 Subject: [PATCH 04/19] Reduce repeated block-change-event related code the new helper code reveals even more repetition, but this is at least consistent now. --- src/block/BaseCoral.php | 8 +- src/block/Cactus.php | 9 +-- src/block/CaveVines.php | 26 ++---- src/block/CocoaBlock.php | 9 +-- src/block/ConcretePowder.php | 8 +- src/block/CoralBlock.php | 8 +- src/block/Crops.php | 14 +--- src/block/Fire.php | 11 +-- src/block/FrostedIce.php | 8 +- src/block/GlowLichen.php | 11 +-- src/block/Grass.php | 14 +--- src/block/Ice.php | 8 +- src/block/Liquid.php | 10 +-- src/block/Mycelium.php | 8 +- src/block/NetherWartPlant.php | 8 +- src/block/SnowLayer.php | 8 +- src/block/Stem.php | 15 +--- src/block/Sugarcane.php | 10 +-- src/block/SweetBerryBush.php | 16 +--- src/block/utils/BlockEventHelper.php | 115 +++++++++++++++++++++++++++ 20 files changed, 166 insertions(+), 158 deletions(-) create mode 100644 src/block/utils/BlockEventHelper.php diff --git a/src/block/BaseCoral.php b/src/block/BaseCoral.php index 73b2c48d67..3a6acd5d15 100644 --- a/src/block/BaseCoral.php +++ b/src/block/BaseCoral.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\CoralType; use pocketmine\block\utils\CoralTypeTrait; use pocketmine\block\utils\SupportType; -use pocketmine\event\block\BlockDeathEvent; use pocketmine\item\Item; use function mt_rand; @@ -46,11 +46,7 @@ abstract class BaseCoral extends Transparent{ public function onScheduledUpdate() : void{ if(!$this->dead && !$this->isCoveredWithWater()){ - $ev = new BlockDeathEvent($this, $this->setDead(true)); - $ev->call(); - if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::die($this, (clone $this)->setDead(true)); } } diff --git a/src/block/Cactus.php b/src/block/Cactus.php index 8fff294f6f..0176d17bd6 100644 --- a/src/block/Cactus.php +++ b/src/block/Cactus.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\entity\Entity; -use pocketmine\event\block\BlockGrowEvent; use pocketmine\event\entity\EntityDamageByBlockEvent; use pocketmine\event\entity\EntityDamageEvent; use pocketmine\item\Item; @@ -111,12 +111,7 @@ class Cactus extends Transparent{ } $b = $world->getBlockAt($this->position->x, $this->position->y + $y, $this->position->z); if($b->getTypeId() === BlockTypeIds::AIR){ - $ev = new BlockGrowEvent($b, VanillaBlocks::CACTUS()); - $ev->call(); - if($ev->isCancelled()){ - break; - } - $world->setBlock($b->position, $ev->getNewState()); + BlockEventHelper::grow($b, VanillaBlocks::CACTUS(), null); }else{ break; } diff --git a/src/block/CaveVines.php b/src/block/CaveVines.php index e56b8b720b..6ff934881d 100644 --- a/src/block/CaveVines.php +++ b/src/block/CaveVines.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\entity\Entity; -use pocketmine\event\block\BlockGrowEvent; use pocketmine\item\Fertilizer; use pocketmine\item\Item; use pocketmine\item\VanillaItems; @@ -114,16 +114,12 @@ class CaveVines extends Flowable{ return true; } if($item instanceof Fertilizer){ - $ev = new BlockGrowEvent($this, (clone $this) + $newState = (clone $this) ->setBerries(true) - ->setHead(!$this->getSide(Facing::DOWN)->hasSameTypeId($this)) - ); - $ev->call(); - if($ev->isCancelled()){ - return false; + ->setHead(!$this->getSide(Facing::DOWN)->hasSameTypeId($this)); + if(BlockEventHelper::grow($this, $newState, $player)){ + $item->pop(); } - $item->pop(); - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); return true; } return false; @@ -141,16 +137,10 @@ class CaveVines extends Flowable{ if($world->isInWorld($growthPos->getFloorX(), $growthPos->getFloorY(), $growthPos->getFloorZ())){ $block = $world->getBlock($growthPos); if($block->getTypeId() === BlockTypeIds::AIR){ - $ev = new BlockGrowEvent($block, VanillaBlocks::CAVE_VINES() + $newState = VanillaBlocks::CAVE_VINES() ->setAge($this->age + 1) - ->setBerries(mt_rand(1, 9) === 1) - ); - - $ev->call(); - - if(!$ev->isCancelled()){ - $world->setBlock($growthPos, $ev->getNewState()); - } + ->setBerries(mt_rand(1, 9) === 1); + BlockEventHelper::grow($block, $newState, null); } } } diff --git a/src/block/CocoaBlock.php b/src/block/CocoaBlock.php index aafce31693..69f94eaf6f 100644 --- a/src/block/CocoaBlock.php +++ b/src/block/CocoaBlock.php @@ -23,11 +23,11 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\block\utils\WoodType; use pocketmine\data\runtime\RuntimeDataDescriber; -use pocketmine\event\block\BlockGrowEvent; use pocketmine\item\Fertilizer; use pocketmine\item\Item; use pocketmine\item\VanillaItems; @@ -123,12 +123,7 @@ class CocoaBlock extends Transparent{ if($this->age < self::MAX_AGE){ $block = clone $this; $block->age++; - $ev = new BlockGrowEvent($this, $block, $player); - $ev->call(); - if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); - return true; - } + return BlockEventHelper::grow($this, $block, $player); } return false; } diff --git a/src/block/ConcretePowder.php b/src/block/ConcretePowder.php index 02fa31adbe..635d3ff2cf 100644 --- a/src/block/ConcretePowder.php +++ b/src/block/ConcretePowder.php @@ -23,11 +23,11 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\ColoredTrait; use pocketmine\block\utils\DyeColor; use pocketmine\block\utils\Fallable; use pocketmine\block\utils\FallableTrait; -use pocketmine\event\block\BlockFormEvent; use pocketmine\math\Facing; class ConcretePowder extends Opaque implements Fallable{ @@ -43,11 +43,7 @@ class ConcretePowder extends Opaque implements Fallable{ public function onNearbyBlockChange() : void{ if(($water = $this->getAdjacentWater()) !== null){ - $ev = new BlockFormEvent($this, VanillaBlocks::CONCRETE()->setColor($this->color), $water); - $ev->call(); - if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::form($this, VanillaBlocks::CONCRETE()->setColor($this->color), $water); }else{ $this->startFalling(); } diff --git a/src/block/CoralBlock.php b/src/block/CoralBlock.php index b902465617..3aaf5b7528 100644 --- a/src/block/CoralBlock.php +++ b/src/block/CoralBlock.php @@ -23,9 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\CoralType; use pocketmine\block\utils\CoralTypeTrait; -use pocketmine\event\block\BlockDeathEvent; use pocketmine\item\Item; use function mt_rand; @@ -55,11 +55,7 @@ final class CoralBlock extends Opaque{ } } if(!$hasWater){ - $ev = new BlockDeathEvent($this, $this->setDead(true)); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::die($this, (clone $this)->setDead(true)); } } } diff --git a/src/block/Crops.php b/src/block/Crops.php index 8949e663b1..d6e84c4243 100644 --- a/src/block/Crops.php +++ b/src/block/Crops.php @@ -23,8 +23,8 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\data\runtime\RuntimeDataDescriber; -use pocketmine\event\block\BlockGrowEvent; use pocketmine\item\Fertilizer; use pocketmine\item\Item; use pocketmine\math\Facing; @@ -68,11 +68,7 @@ abstract class Crops extends Flowable{ if($block->age > self::MAX_AGE){ $block->age = self::MAX_AGE; } - - $ev = new BlockGrowEvent($this, $block, $player); - $ev->call(); - if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); + if(BlockEventHelper::grow($this, $block, $player)){ $item->pop(); } @@ -96,11 +92,7 @@ abstract class Crops extends Flowable{ if($this->age < self::MAX_AGE && mt_rand(0, 2) === 1){ $block = clone $this; ++$block->age; - $ev = new BlockGrowEvent($this, $block); - $ev->call(); - if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::grow($this, $block, null); } } } diff --git a/src/block/Fire.php b/src/block/Fire.php index 0a3b2e3e37..a39ef6b9cc 100644 --- a/src/block/Fire.php +++ b/src/block/Fire.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\event\block\BlockBurnEvent; -use pocketmine\event\block\BlockSpreadEvent; use pocketmine\math\Facing; use pocketmine\world\format\Chunk; use pocketmine\world\World; @@ -225,13 +225,6 @@ class Fire extends BaseFire{ } private function spreadBlock(Block $block, Block $newState) : bool{ - $ev = new BlockSpreadEvent($block, $this, $newState); - $ev->call(); - if(!$ev->isCancelled()){ - $block->position->getWorld()->setBlock($block->position, $ev->getNewState()); - return true; - } - - return false; + return BlockEventHelper::spread($block, $newState, $this); } } diff --git a/src/block/FrostedIce.php b/src/block/FrostedIce.php index 38c552a954..039fe49f36 100644 --- a/src/block/FrostedIce.php +++ b/src/block/FrostedIce.php @@ -23,8 +23,8 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\data\runtime\RuntimeDataDescriber; -use pocketmine\event\block\BlockMeltEvent; use function mt_rand; class FrostedIce extends Ice{ @@ -97,11 +97,7 @@ class FrostedIce extends Ice{ private function tryMelt() : bool{ $world = $this->position->getWorld(); if($this->age >= self::MAX_AGE){ - $ev = new BlockMeltEvent($this, VanillaBlocks::WATER()); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::melt($this, VanillaBlocks::WATER()); return true; } diff --git a/src/block/GlowLichen.php b/src/block/GlowLichen.php index 39ce512a69..84dec29ce9 100644 --- a/src/block/GlowLichen.php +++ b/src/block/GlowLichen.php @@ -23,9 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; -use pocketmine\event\block\BlockSpreadEvent; use pocketmine\item\Fertilizer; use pocketmine\item\Item; use pocketmine\math\AxisAlignedBB; @@ -166,14 +166,7 @@ class GlowLichen extends Transparent{ return false; } - $ev = new BlockSpreadEvent($replacedBlock, $this, $replacementBlock); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($replacedBlock->getPosition(), $ev->getNewState()); - return true; - } - - return false; + return BlockEventHelper::spread($replacedBlock, $replacementBlock, $this); } /** diff --git a/src/block/Grass.php b/src/block/Grass.php index 54975d0460..709dc6a9de 100644 --- a/src/block/Grass.php +++ b/src/block/Grass.php @@ -23,8 +23,8 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\DirtType; -use pocketmine\event\block\BlockSpreadEvent; use pocketmine\item\Fertilizer; use pocketmine\item\Hoe; use pocketmine\item\Item; @@ -58,11 +58,7 @@ class Grass extends Opaque{ $lightAbove = $world->getFullLightAt($this->position->x, $this->position->y + 1, $this->position->z); if($lightAbove < 4 && $world->getBlockAt($this->position->x, $this->position->y + 1, $this->position->z)->getLightFilter() >= 2){ //grass dies - $ev = new BlockSpreadEvent($this, $this, VanillaBlocks::DIRT()); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($this->position, $ev->getNewState(), false); - } + BlockEventHelper::spread($this, VanillaBlocks::DIRT(), $this); }elseif($lightAbove >= 9){ //try grass spread for($i = 0; $i < 4; ++$i){ @@ -80,11 +76,7 @@ class Grass extends Opaque{ continue; } - $ev = new BlockSpreadEvent($b, $this, VanillaBlocks::GRASS()); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($b->position, $ev->getNewState(), false); - } + BlockEventHelper::spread($b, VanillaBlocks::GRASS(), $this); } } } diff --git a/src/block/Ice.php b/src/block/Ice.php index 41f60e3f9d..ca0302f906 100644 --- a/src/block/Ice.php +++ b/src/block/Ice.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; -use pocketmine\event\block\BlockMeltEvent; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\item\enchantment\VanillaEnchantments; use pocketmine\item\Item; use pocketmine\player\Player; @@ -53,11 +53,7 @@ class Ice extends Transparent{ public function onRandomTick() : void{ $world = $this->position->getWorld(); if($world->getHighestAdjacentBlockLight($this->position->x, $this->position->y, $this->position->z) >= 12){ - $ev = new BlockMeltEvent($this, VanillaBlocks::WATER()); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::melt($this, VanillaBlocks::WATER()); } } diff --git a/src/block/Liquid.php b/src/block/Liquid.php index a56f666a2c..e102540ecf 100644 --- a/src/block/Liquid.php +++ b/src/block/Liquid.php @@ -23,11 +23,11 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\MinimumCostFlowCalculator; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\entity\Entity; -use pocketmine\event\block\BlockFormEvent; use pocketmine\event\block\BlockSpreadEvent; use pocketmine\item\Item; use pocketmine\math\AxisAlignedBB; @@ -363,12 +363,8 @@ abstract class Liquid extends Transparent{ } protected function liquidCollide(Block $cause, Block $result) : bool{ - $ev = new BlockFormEvent($this, $result, $cause); - $ev->call(); - if(!$ev->isCancelled()){ - $world = $this->position->getWorld(); - $world->setBlock($this->position, $ev->getNewState()); - $world->addSound($this->position->add(0.5, 0.5, 0.5), new FizzSound(2.6 + (lcg_value() - lcg_value()) * 0.8)); + if(BlockEventHelper::form($this, $result, $cause)){ + $this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new FizzSound(2.6 + (lcg_value() - lcg_value()) * 0.8)); } return true; } diff --git a/src/block/Mycelium.php b/src/block/Mycelium.php index c87f893678..08b0b3e750 100644 --- a/src/block/Mycelium.php +++ b/src/block/Mycelium.php @@ -23,8 +23,8 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\DirtType; -use pocketmine\event\block\BlockSpreadEvent; use pocketmine\item\Item; use pocketmine\math\Facing; use function mt_rand; @@ -54,11 +54,7 @@ class Mycelium extends Opaque{ $block = $world->getBlockAt($x, $y, $z); if($block instanceof Dirt && $block->getDirtType()->equals(DirtType::NORMAL())){ if($block->getSide(Facing::UP) instanceof Transparent){ - $ev = new BlockSpreadEvent($block, $this, VanillaBlocks::MYCELIUM()); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($block->position, $ev->getNewState()); - } + BlockEventHelper::spread($block, VanillaBlocks::MYCELIUM(), $this); } } } diff --git a/src/block/NetherWartPlant.php b/src/block/NetherWartPlant.php index 6a8fe1f7a2..d7e5874417 100644 --- a/src/block/NetherWartPlant.php +++ b/src/block/NetherWartPlant.php @@ -23,9 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\FortuneDropHelper; use pocketmine\data\runtime\RuntimeDataDescriber; -use pocketmine\event\block\BlockGrowEvent; use pocketmine\item\Item; use pocketmine\math\Facing; use pocketmine\math\Vector3; @@ -76,11 +76,7 @@ class NetherWartPlant extends Flowable{ if($this->age < self::MAX_AGE && mt_rand(0, 10) === 0){ //Still growing $block = clone $this; $block->age++; - $ev = new BlockGrowEvent($this, $block); - $ev->call(); - if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::grow($this, $block, null); } } diff --git a/src/block/SnowLayer.php b/src/block/SnowLayer.php index f561c8ff52..05fc884213 100644 --- a/src/block/SnowLayer.php +++ b/src/block/SnowLayer.php @@ -23,11 +23,11 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\Fallable; use pocketmine\block\utils\FallableTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; -use pocketmine\event\block\BlockMeltEvent; use pocketmine\item\Item; use pocketmine\item\VanillaItems; use pocketmine\math\AxisAlignedBB; @@ -105,11 +105,7 @@ class SnowLayer extends Flowable implements Fallable{ public function onRandomTick() : void{ $world = $this->position->getWorld(); if($world->getBlockLightAt($this->position->x, $this->position->y, $this->position->z) >= 12){ - $ev = new BlockMeltEvent($this, VanillaBlocks::AIR()); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::melt($this, VanillaBlocks::AIR()); } } diff --git a/src/block/Stem.php b/src/block/Stem.php index 252b28aebc..7223572dd4 100644 --- a/src/block/Stem.php +++ b/src/block/Stem.php @@ -23,8 +23,8 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\data\runtime\RuntimeDataDescriber; -use pocketmine\event\block\BlockGrowEvent; use pocketmine\item\Item; use pocketmine\math\Facing; use function array_rand; @@ -64,11 +64,7 @@ abstract class Stem extends Crops{ if($this->age < self::MAX_AGE){ $block = clone $this; ++$block->age; - $ev = new BlockGrowEvent($this, $block); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::grow($this, $block, null); }else{ $grow = $this->getPlant(); foreach(Facing::HORIZONTAL as $side){ @@ -80,12 +76,7 @@ abstract class Stem extends Crops{ $facing = Facing::HORIZONTAL[array_rand(Facing::HORIZONTAL)]; $side = $this->getSide($facing); if($side->getTypeId() === BlockTypeIds::AIR && $side->getSide(Facing::DOWN)->hasTypeTag(BlockTypeTags::DIRT)){ - $ev = new BlockGrowEvent($side, $grow); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($this->position, $this->setFacing($facing)); - $world->setBlock($side->position, $ev->getNewState()); - } + BlockEventHelper::grow($side, $grow, null); } } } diff --git a/src/block/Sugarcane.php b/src/block/Sugarcane.php index 4cc5989a79..3757e45772 100644 --- a/src/block/Sugarcane.php +++ b/src/block/Sugarcane.php @@ -23,8 +23,8 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\data\runtime\RuntimeDataDescriber; -use pocketmine\event\block\BlockGrowEvent; use pocketmine\item\Fertilizer; use pocketmine\item\Item; use pocketmine\math\Facing; @@ -60,13 +60,11 @@ class Sugarcane extends Flowable{ } $b = $world->getBlockAt($pos->x, $pos->y + $y, $pos->z); if($b->getTypeId() === BlockTypeIds::AIR){ - $ev = new BlockGrowEvent($b, VanillaBlocks::SUGARCANE(), $player); - $ev->call(); - if($ev->isCancelled()){ + if(BlockEventHelper::grow($b, VanillaBlocks::SUGARCANE(), $player)){ + $grew = true; + }else{ break; } - $world->setBlock($b->position, $ev->getNewState()); - $grew = true; }elseif(!$b->hasSameTypeId($this)){ break; } diff --git a/src/block/SweetBerryBush.php b/src/block/SweetBerryBush.php index 5ead39390b..ef1169df53 100644 --- a/src/block/SweetBerryBush.php +++ b/src/block/SweetBerryBush.php @@ -23,11 +23,11 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\FortuneDropHelper; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\entity\Entity; use pocketmine\entity\Living; -use pocketmine\event\block\BlockGrowEvent; use pocketmine\event\entity\EntityDamageByBlockEvent; use pocketmine\item\Fertilizer; use pocketmine\item\Item; @@ -87,15 +87,9 @@ class SweetBerryBush extends Flowable{ if($this->age < self::STAGE_MATURE && $item instanceof Fertilizer){ $block = clone $this; $block->age++; - - $ev = new BlockGrowEvent($this, $block, $player); - $ev->call(); - - if(!$ev->isCancelled()){ - $world->setBlock($this->position, $ev->getNewState()); + if(BlockEventHelper::grow($this, $block, $player)){ $item->pop(); } - }elseif(($dropAmount = $this->getBerryDropAmount()) > 0){ $world->setBlock($this->position, $this->setAge(self::STAGE_BUSH_NO_BERRIES)); $world->dropItem($this->position, $this->asItem()->setCount($dropAmount)); @@ -133,11 +127,7 @@ class SweetBerryBush extends Flowable{ if($this->age < self::STAGE_MATURE && mt_rand(0, 2) === 1){ $block = clone $this; ++$block->age; - $ev = new BlockGrowEvent($this, $block); - $ev->call(); - if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::grow($this, $block, null); } } diff --git a/src/block/utils/BlockEventHelper.php b/src/block/utils/BlockEventHelper.php new file mode 100644 index 0000000000..fc9b06d13c --- /dev/null +++ b/src/block/utils/BlockEventHelper.php @@ -0,0 +1,115 @@ +call(); + if($ev->isCancelled()){ + return false; + } + $newState = $ev->getNewState(); + } + + $position = $oldState->getPosition(); + $position->getWorld()->setBlock($position, $newState); + return true; + } + + public static function spread(Block $oldState, Block $newState, Block $source) : bool{ + if(BlockSpreadEvent::hasHandlers()){ + $ev = new BlockSpreadEvent($oldState, $source, $newState); + $ev->call(); + if($ev->isCancelled()){ + return false; + } + $newState = $ev->getNewState(); + } + + $position = $oldState->getPosition(); + $position->getWorld()->setBlock($position, $newState); + return true; + } + + public static function form(Block $oldState, Block $newState, Block $cause) : bool{ + if(BlockFormEvent::hasHandlers()){ + $ev = new BlockFormEvent($oldState, $newState, $cause); + $ev->call(); + if($ev->isCancelled()){ + return false; + } + $newState = $ev->getNewState(); + } + + $position = $oldState->getPosition(); + $position->getWorld()->setBlock($position, $newState); + return true; + } + + public static function melt(Block $oldState, Block $newState) : bool{ + if(BlockMeltEvent::hasHandlers()){ + $ev = new BlockMeltEvent($oldState, $newState); + $ev->call(); + if($ev->isCancelled()){ + return false; + } + $newState = $ev->getNewState(); + } + + $position = $oldState->getPosition(); + $position->getWorld()->setBlock($position, $newState); + return true; + } + + public static function die(Block $oldState, Block $newState) : bool{ + if(BlockDeathEvent::hasHandlers()){ + $ev = new BlockDeathEvent($oldState, $newState); + $ev->call(); + if($ev->isCancelled()){ + return false; + } + $newState = $ev->getNewState(); + } + + $position = $oldState->getPosition(); + $position->getWorld()->setBlock($position, $newState); + return true; + } +} From 1a2c10e844aa330e5ca99800669313f3deaa4657 Mon Sep 17 00:00:00 2001 From: jasonw_4331 Date: Wed, 2 Aug 2023 13:05:16 -0400 Subject: [PATCH 05/19] World: Fixed getSafeSpawn() not accepting seed positions below y=1 (#5955) this should have been changed during the introduction of y=-64 minimum world height, but it got missed. --- src/world/World.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/world/World.php b/src/world/World.php index f556d2e1f2..b09304742d 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -2951,7 +2951,7 @@ class World implements ChunkManager{ * @throws WorldException if the terrain is not generated */ public function getSafeSpawn(?Vector3 $spawn = null) : Position{ - if(!($spawn instanceof Vector3) || $spawn->y < 1){ + if(!($spawn instanceof Vector3) || $spawn->y <= $this->minY){ $spawn = $this->getSpawnLocation(); } From 81941ae9e5dbbcfccedafc31f6acf3a258873b45 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Aug 2023 18:05:30 +0100 Subject: [PATCH 06/19] Bump phpunit/phpunit from 10.2.6 to 10.2.7 (#5957) Bumps [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) from 10.2.6 to 10.2.7. - [Changelog](https://github.com/sebastianbergmann/phpunit/blob/10.2.7/ChangeLog-10.2.md) - [Commits](https://github.com/sebastianbergmann/phpunit/compare/10.2.6...10.2.7) --- updated-dependencies: - dependency-name: phpunit/phpunit dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- composer.lock | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/composer.lock b/composer.lock index a97b576eeb..8d65486e77 100644 --- a/composer.lock +++ b/composer.lock @@ -1541,16 +1541,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "10.1.2", + "version": "10.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "db1497ec8dd382e82c962f7abbe0320e4882ee4e" + "reference": "be1fe461fdc917de2a29a452ccf2657d325b443d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/db1497ec8dd382e82c962f7abbe0320e4882ee4e", - "reference": "db1497ec8dd382e82c962f7abbe0320e4882ee4e", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/be1fe461fdc917de2a29a452ccf2657d325b443d", + "reference": "be1fe461fdc917de2a29a452ccf2657d325b443d", "shasum": "" }, "require": { @@ -1607,7 +1607,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.2" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.3" }, "funding": [ { @@ -1615,7 +1615,7 @@ "type": "github" } ], - "time": "2023-05-22T09:04:27+00:00" + "time": "2023-07-26T13:45:28+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1861,16 +1861,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.2.6", + "version": "10.2.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "1c17815c129f133f3019cc18e8d0c8622e6d9bcd" + "reference": "a215d9ee8bac1733796e4ddff3306811f14414e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1c17815c129f133f3019cc18e8d0c8622e6d9bcd", - "reference": "1c17815c129f133f3019cc18e8d0c8622e6d9bcd", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a215d9ee8bac1733796e4ddff3306811f14414e5", + "reference": "a215d9ee8bac1733796e4ddff3306811f14414e5", "shasum": "" }, "require": { @@ -1895,7 +1895,7 @@ "sebastian/diff": "^5.0", "sebastian/environment": "^6.0", "sebastian/exporter": "^5.0", - "sebastian/global-state": "^6.0", + "sebastian/global-state": "^6.0.1", "sebastian/object-enumerator": "^5.0", "sebastian/recursion-context": "^5.0", "sebastian/type": "^4.0", @@ -1942,7 +1942,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.2.6" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.2.7" }, "funding": [ { @@ -1958,7 +1958,7 @@ "type": "tidelift" } ], - "time": "2023-07-17T12:08:28+00:00" + "time": "2023-08-02T06:46:08+00:00" }, { "name": "sebastian/cli-parser", From c91c8c2f9e915e3ba67216cf77c76cb59de9bda8 Mon Sep 17 00:00:00 2001 From: Dylan T Date: Thu, 3 Aug 2023 14:51:51 +0100 Subject: [PATCH 07/19] Improving performance of small moving entities (e.g. dropped items) (#5954) * World: cache block AABBs directly in the world this removes some indirection when fetching the AABBs, and also allows the AABB cache to live longer than the block cache. In local testing this showed a 10-20% performance improvement, but it was difficult to properly measure. * World: eliminate padding block checks in getCollisionBoxes() this substantially improves the function's performance for small entities. The padding of 1 block in each direction was previously necessary to account for blocks like fences, which might have an AABB larger than the cell containing them. However, by tracking this information in the collisionBoxCache directly, we can avoid the need to check this at the expense of slightly more complex code. This reduces the number of blocks checked for a moving item entity from 27-64 all the way down to 1-8, which is a major improvement. Locally, this change allowed me to simulate 2100 item entities without lag, compared with 1500 on the previous commit. --- src/world/World.php | 79 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 8 deletions(-) diff --git a/src/world/World.php b/src/world/World.php index f556d2e1f2..4f266022f1 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -200,6 +200,11 @@ class World implements ChunkManager{ * @phpstan-var array> */ private array $blockCache = []; + /** + * @var AxisAlignedBB[][][] chunkHash => [relativeBlockHash => AxisAlignedBB[]] + * @phpstan-var array>> + */ + private array $blockCollisionBoxCache = []; private int $sendTimeTicker = 0; @@ -646,6 +651,7 @@ class World implements ChunkManager{ $this->provider->close(); $this->blockCache = []; + $this->blockCollisionBoxCache = []; $this->unloaded = true; } @@ -1117,6 +1123,7 @@ class World implements ChunkManager{ public function clearCache(bool $force = false) : void{ if($force){ $this->blockCache = []; + $this->blockCollisionBoxCache = []; }else{ $count = 0; foreach($this->blockCache as $list){ @@ -1126,6 +1133,16 @@ class World implements ChunkManager{ break; } } + + $count = 0; + foreach($this->blockCollisionBoxCache as $list){ + $count += count($list); + if($count > 2048){ + //TODO: Is this really the best logic? + $this->blockCollisionBoxCache = []; + break; + } + } } } @@ -1491,25 +1508,53 @@ class World implements ChunkManager{ return $collides; } + /** + * Returns a list of all block AABBs which overlap the full block area at the given coordinates. + * This checks a padding of 1 block around the coordinates to account for oversized AABBs of blocks like fences. + * Larger AABBs (>= 2 blocks on any axis) are not accounted for. + * + * @return AxisAlignedBB[] + */ + private function getCollisionBoxesForCell(int $x, int $y, int $z) : array{ + $block = $this->getBlockAt($x, $y, $z); + $boxes = $block->getCollisionBoxes(); + + $cellBB = AxisAlignedBB::one()->offset($x, $y, $z); + foreach(Facing::ALL as $facing){ + $extraBoxes = $block->getSide($facing)->getCollisionBoxes(); + foreach($extraBoxes as $extraBox){ + if($extraBox->intersectsWith($cellBB)){ + $boxes[] = $extraBox; + } + } + } + + return $boxes; + } + /** * @return AxisAlignedBB[] * @phpstan-return list */ public function getCollisionBoxes(Entity $entity, AxisAlignedBB $bb, bool $entities = true) : array{ - $minX = (int) floor($bb->minX - 1); - $minY = (int) floor($bb->minY - 1); - $minZ = (int) floor($bb->minZ - 1); - $maxX = (int) floor($bb->maxX + 1); - $maxY = (int) floor($bb->maxY + 1); - $maxZ = (int) floor($bb->maxZ + 1); + $minX = (int) floor($bb->minX); + $minY = (int) floor($bb->minY); + $minZ = (int) floor($bb->minZ); + $maxX = (int) floor($bb->maxX); + $maxY = (int) floor($bb->maxY); + $maxZ = (int) floor($bb->maxZ); $collides = []; for($z = $minZ; $z <= $maxZ; ++$z){ for($x = $minX; $x <= $maxX; ++$x){ + $chunkPosHash = World::chunkHash($x >> Chunk::COORD_BIT_SIZE, $z >> Chunk::COORD_BIT_SIZE); for($y = $minY; $y <= $maxY; ++$y){ - $block = $this->getBlockAt($x, $y, $z); - foreach($block->getCollisionBoxes() as $blockBB){ + $relativeBlockHash = World::chunkBlockHash($x, $y, $z); + + $boxes = $this->blockCollisionBoxCache[$chunkPosHash][$relativeBlockHash] ??= $this->getCollisionBoxesForCell($x, $y, $z); + + foreach($boxes as $blockBB){ if($blockBB->intersectsWith($bb)){ $collides[] = $blockBB; } @@ -1858,6 +1903,21 @@ class World implements ChunkManager{ $relativeBlockHash = World::chunkBlockHash($x, $y, $z); unset($this->blockCache[$chunkHash][$relativeBlockHash]); + unset($this->blockCollisionBoxCache[$chunkHash][$relativeBlockHash]); + //blocks like fences have collision boxes that reach into neighbouring blocks, so we need to invalidate the + //caches for those blocks as well + foreach([ + [0, 0, 1], + [0, 0, -1], + [0, 1, 0], + [0, -1, 0], + [1, 0, 0], + [-1, 0, 0] + ] as [$offsetX, $offsetY, $offsetZ]){ + $sideChunkPosHash = World::chunkHash(($x + $offsetX) >> Chunk::COORD_BIT_SIZE, ($z + $offsetZ) >> Chunk::COORD_BIT_SIZE); + $sideChunkBlockHash = World::chunkBlockHash($x + $offsetX, $y + $offsetY, $z + $offsetZ); + unset($this->blockCollisionBoxCache[$sideChunkPosHash][$sideChunkBlockHash]); + } if(!isset($this->changedBlocks[$chunkHash])){ $this->changedBlocks[$chunkHash] = []; @@ -2454,6 +2514,7 @@ class World implements ChunkManager{ $this->chunks[$chunkHash] = $chunk; unset($this->blockCache[$chunkHash]); + unset($this->blockCollisionBoxCache[$chunkHash]); unset($this->changedBlocks[$chunkHash]); $chunk->setTerrainDirty(); $this->markTickingChunkForRecheck($chunkX, $chunkZ); //this replacement chunk may not meet the conditions for ticking @@ -2733,6 +2794,7 @@ class World implements ChunkManager{ } $this->chunks[$chunkHash] = $chunk; unset($this->blockCache[$chunkHash]); + unset($this->blockCollisionBoxCache[$chunkHash]); $this->initChunk($x, $z, $chunkData); @@ -2887,6 +2949,7 @@ class World implements ChunkManager{ unset($this->chunks[$chunkHash]); unset($this->blockCache[$chunkHash]); + unset($this->blockCollisionBoxCache[$chunkHash]); unset($this->changedBlocks[$chunkHash]); unset($this->registeredTickingChunks[$chunkHash]); $this->markTickingChunkForRecheck($x, $z); From 6ac45526f9b58a068d0be0ffd9fa1bce771f8eb5 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 3 Aug 2023 16:46:16 +0100 Subject: [PATCH 08/19] Use new features in pocketmine/math 1.0.0 --- composer.json | 2 +- composer.lock | 58 ++++++++++++++++----------------- src/block/MobHead.php | 17 +++++----- src/world/World.php | 9 +---- src/world/light/LightUpdate.php | 16 +++------ 5 files changed, 44 insertions(+), 58 deletions(-) diff --git a/composer.json b/composer.json index 5cb32d2ac6..f191086ca7 100644 --- a/composer.json +++ b/composer.json @@ -43,7 +43,7 @@ "pocketmine/errorhandler": "^0.6.0", "pocketmine/locale-data": "~2.19.0", "pocketmine/log": "^0.4.0", - "pocketmine/math": "^0.4.0", + "pocketmine/math": "~1.0.0", "pocketmine/nbt": "^0.3.2", "pocketmine/raklib": "^0.15.0", "pocketmine/raklib-ipc": "^0.2.0", diff --git a/composer.lock b/composer.lock index a97b576eeb..40bdcb23d3 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ccd20e7656bc05ec2acd8e28aad9fcf2", + "content-hash": "63ecdcea88960659a6977300a0ea79b3", "packages": [ { "name": "adhocore/json-comment", @@ -200,16 +200,16 @@ }, { "name": "pocketmine/bedrock-protocol", - "version": "23.0.2+bedrock-1.20.10", + "version": "23.0.3+bedrock-1.20.10", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "69a309a2dd7dcf3ec8c316385b866397e8c2cbfd" + "reference": "e4157c7af3f91e1b08fe21be171eb73dad7029e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/69a309a2dd7dcf3ec8c316385b866397e8c2cbfd", - "reference": "69a309a2dd7dcf3ec8c316385b866397e8c2cbfd", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/e4157c7af3f91e1b08fe21be171eb73dad7029e9", + "reference": "e4157c7af3f91e1b08fe21be171eb73dad7029e9", "shasum": "" }, "require": { @@ -218,8 +218,8 @@ "php": "^8.0", "pocketmine/binaryutils": "^0.2.0", "pocketmine/color": "^0.2.0 || ^0.3.0", - "pocketmine/math": "^0.3.0 || ^0.4.0", - "pocketmine/nbt": "^0.3.0", + "pocketmine/math": "^0.3.0 || ^0.4.0 || ^1.0.0", + "pocketmine/nbt": "^0.3.0 || ^1.0.0", "ramsey/uuid": "^4.1" }, "require-dev": { @@ -241,9 +241,9 @@ "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", "support": { "issues": "https://github.com/pmmp/BedrockProtocol/issues", - "source": "https://github.com/pmmp/BedrockProtocol/tree/23.0.2+bedrock-1.20.10" + "source": "https://github.com/pmmp/BedrockProtocol/tree/23.0.3+bedrock-1.20.10" }, - "time": "2023-07-24T15:35:36+00:00" + "time": "2023-08-03T15:30:52+00:00" }, { "name": "pocketmine/binaryutils", @@ -480,16 +480,16 @@ }, { "name": "pocketmine/math", - "version": "0.4.3", + "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/pmmp/Math.git", - "reference": "47a243d320b01c8099d65309967934c188111549" + "reference": "dc132d93595b32e9f210d78b3c8d43c662a5edbf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/Math/zipball/47a243d320b01c8099d65309967934c188111549", - "reference": "47a243d320b01c8099d65309967934c188111549", + "url": "https://api.github.com/repos/pmmp/Math/zipball/dc132d93595b32e9f210d78b3c8d43c662a5edbf", + "reference": "dc132d93595b32e9f210d78b3c8d43c662a5edbf", "shasum": "" }, "require": { @@ -498,7 +498,7 @@ }, "require-dev": { "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "1.8.2", + "phpstan/phpstan": "~1.10.3", "phpstan/phpstan-strict-rules": "^1.0", "phpunit/phpunit": "^8.5 || ^9.5" }, @@ -515,9 +515,9 @@ "description": "PHP library containing math related code used in PocketMine-MP", "support": { "issues": "https://github.com/pmmp/Math/issues", - "source": "https://github.com/pmmp/Math/tree/0.4.3" + "source": "https://github.com/pmmp/Math/tree/1.0.0" }, - "time": "2022-08-25T18:43:37+00:00" + "time": "2023-08-03T12:56:33+00:00" }, { "name": "pocketmine/nbt", @@ -1541,16 +1541,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "10.1.2", + "version": "10.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "db1497ec8dd382e82c962f7abbe0320e4882ee4e" + "reference": "be1fe461fdc917de2a29a452ccf2657d325b443d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/db1497ec8dd382e82c962f7abbe0320e4882ee4e", - "reference": "db1497ec8dd382e82c962f7abbe0320e4882ee4e", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/be1fe461fdc917de2a29a452ccf2657d325b443d", + "reference": "be1fe461fdc917de2a29a452ccf2657d325b443d", "shasum": "" }, "require": { @@ -1607,7 +1607,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.2" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.3" }, "funding": [ { @@ -1615,7 +1615,7 @@ "type": "github" } ], - "time": "2023-05-22T09:04:27+00:00" + "time": "2023-07-26T13:45:28+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1861,16 +1861,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.2.6", + "version": "10.2.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "1c17815c129f133f3019cc18e8d0c8622e6d9bcd" + "reference": "a215d9ee8bac1733796e4ddff3306811f14414e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1c17815c129f133f3019cc18e8d0c8622e6d9bcd", - "reference": "1c17815c129f133f3019cc18e8d0c8622e6d9bcd", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a215d9ee8bac1733796e4ddff3306811f14414e5", + "reference": "a215d9ee8bac1733796e4ddff3306811f14414e5", "shasum": "" }, "require": { @@ -1895,7 +1895,7 @@ "sebastian/diff": "^5.0", "sebastian/environment": "^6.0", "sebastian/exporter": "^5.0", - "sebastian/global-state": "^6.0", + "sebastian/global-state": "^6.0.1", "sebastian/object-enumerator": "^5.0", "sebastian/recursion-context": "^5.0", "sebastian/type": "^4.0", @@ -1942,7 +1942,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.2.6" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.2.7" }, "funding": [ { @@ -1958,7 +1958,7 @@ "type": "tidelift" } ], - "time": "2023-07-17T12:08:28+00:00" + "time": "2023-08-02T06:46:08+00:00" }, { "name": "sebastian/cli-parser", diff --git a/src/block/MobHead.php b/src/block/MobHead.php index 1b3f2d88fa..96cd1cf344 100644 --- a/src/block/MobHead.php +++ b/src/block/MobHead.php @@ -113,14 +113,15 @@ class MobHead extends Flowable{ * @return AxisAlignedBB[] */ protected function recalculateCollisionBoxes() : array{ - $collisionBox = AxisAlignedBB::one()->contract(0.25, 0, 0.25)->trim(Facing::UP, 0.5); - return match($this->facing){ - Facing::NORTH => [$collisionBox->offset(0, 0.25, 0.25)], - Facing::SOUTH => [$collisionBox->offset(0, 0.25, -0.25)], - Facing::WEST => [$collisionBox->offset(0.25, 0.25, 0)], - Facing::EAST => [$collisionBox->offset(-0.25, 0.25, 0)], - default => [$collisionBox] - }; + $collisionBox = AxisAlignedBB::one() + ->contract(0.25, 0, 0.25) + ->trim(Facing::UP, 0.5); + if($this->facing !== Facing::UP){ + $collisionBox = $collisionBox + ->offsetTowards(Facing::opposite($this->facing), 0.25) + ->offsetTowards(Facing::UP, 0.25); + } + return [$collisionBox]; } public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ diff --git a/src/world/World.php b/src/world/World.php index 4f266022f1..49025b9481 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -1906,14 +1906,7 @@ class World implements ChunkManager{ unset($this->blockCollisionBoxCache[$chunkHash][$relativeBlockHash]); //blocks like fences have collision boxes that reach into neighbouring blocks, so we need to invalidate the //caches for those blocks as well - foreach([ - [0, 0, 1], - [0, 0, -1], - [0, 1, 0], - [0, -1, 0], - [1, 0, 0], - [-1, 0, 0] - ] as [$offsetX, $offsetY, $offsetZ]){ + foreach(Facing::OFFSET as [$offsetX, $offsetY, $offsetZ]){ $sideChunkPosHash = World::chunkHash(($x + $offsetX) >> Chunk::COORD_BIT_SIZE, ($z + $offsetZ) >> Chunk::COORD_BIT_SIZE); $sideChunkBlockHash = World::chunkBlockHash($x + $offsetX, $y + $offsetY, $z + $offsetZ); unset($this->blockCollisionBoxCache[$sideChunkPosHash][$sideChunkBlockHash]); diff --git a/src/world/light/LightUpdate.php b/src/world/light/LightUpdate.php index 1a82bdcef2..58ac0a9dcb 100644 --- a/src/world/light/LightUpdate.php +++ b/src/world/light/LightUpdate.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\world\light; +use pocketmine\math\Facing; use pocketmine\world\format\LightArray; use pocketmine\world\format\SubChunk; use pocketmine\world\utils\SubChunkExplorer; @@ -33,15 +34,6 @@ use function max; //TODO: make light updates asynchronous abstract class LightUpdate{ - private const ADJACENTS = [ - [ 1, 0, 0], - [-1, 0, 0], - [ 0, 1, 0], - [ 0, -1, 0], - [ 0, 0, 1], - [ 0, 0, -1] - ]; - public const BASE_LIGHT_FILTER = 1; /** @@ -78,7 +70,7 @@ abstract class LightUpdate{ protected function getHighestAdjacentLight(int $x, int $y, int $z) : int{ $adjacent = 0; - foreach(self::ADJACENTS as [$ox, $oy, $oz]){ + foreach(Facing::OFFSET as [$ox, $oy, $oz]){ if(($adjacent = max($adjacent, $this->getEffectiveLight($x + $ox, $y + $oy, $z + $oz))) === 15){ break; } @@ -123,7 +115,7 @@ abstract class LightUpdate{ $touched++; [$x, $y, $z, $oldAdjacentLight] = $context->removalQueue->dequeue(); - foreach(self::ADJACENTS as [$ox, $oy, $oz]){ + foreach(Facing::OFFSET as [$ox, $oy, $oz]){ $cx = $x + $ox; $cy = $y + $oy; $cz = $z + $oz; @@ -163,7 +155,7 @@ abstract class LightUpdate{ continue; } - foreach(self::ADJACENTS as [$ox, $oy, $oz]){ + foreach(Facing::OFFSET as [$ox, $oy, $oz]){ $cx = $x + $ox; $cy = $y + $oy; $cz = $z + $oz; From 1cf508abdbda46ab178da7637aaeb0b892e401ac Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 3 Aug 2023 16:51:09 +0100 Subject: [PATCH 09/19] World: use Facing::OFFSET in getHighestAdjacentLight() --- src/world/World.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/world/World.php b/src/world/World.php index 49025b9481..4d6cad154a 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -1727,14 +1727,10 @@ class World implements ChunkManager{ */ private function getHighestAdjacentLight(int $x, int $y, int $z, \Closure $lightGetter) : int{ $max = 0; - foreach([ - [$x + 1, $y, $z], - [$x - 1, $y, $z], - [$x, $y + 1, $z], - [$x, $y - 1, $z], - [$x, $y, $z + 1], - [$x, $y, $z - 1] - ] as [$x1, $y1, $z1]){ + foreach(Facing::OFFSET as [$offsetX, $offsetY, $offsetZ]){ + $x1 = $x + $offsetX; + $y1 = $y + $offsetY; + $z1 = $z + $offsetZ; if( !$this->isInWorld($x1, $y1, $z1) || ($chunk = $this->getChunk($x1 >> Chunk::COORD_BIT_SIZE, $z1 >> Chunk::COORD_BIT_SIZE)) === null || From 35a28300f6c4a83153087cca54bfe2ffa7d9c185 Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Mon, 7 Aug 2023 13:47:29 +0300 Subject: [PATCH 10/19] Podzol should be affected by silk touch (#5969) --- src/block/Podzol.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/block/Podzol.php b/src/block/Podzol.php index 4eb720573d..33285cc0ed 100644 --- a/src/block/Podzol.php +++ b/src/block/Podzol.php @@ -27,6 +27,10 @@ use pocketmine\item\Item; class Podzol extends Opaque{ + public function isAffectedBySilkTouch() : bool{ + return true; + } + public function getDropsForCompatibleTool(Item $item) : array{ return [ VanillaBlocks::DIRT()->asItem() From 515f8eae4ca2b7b0a3e9251904dfe0f3f688a777 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 7 Aug 2023 17:05:45 +0100 Subject: [PATCH 11/19] =?UTF-8?q?=C3=82ResourcePackManager:=20allow=20sett?= =?UTF-8?q?ing=20force=5Fresources=20from=20a=20plugin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/resourcepacks/ResourcePackManager.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/resourcepacks/ResourcePackManager.php b/src/resourcepacks/ResourcePackManager.php index 6429ac06bb..2df6750def 100644 --- a/src/resourcepacks/ResourcePackManager.php +++ b/src/resourcepacks/ResourcePackManager.php @@ -154,6 +154,13 @@ class ResourcePackManager{ return $this->serverForceResources; } + /** + * Sets whether players must accept resource packs in order to join. + */ + public function setResourcePacksRequired(bool $value) : void{ + $this->serverForceResources = $value; + } + /** * Returns an array of resource packs in use, sorted in order of priority. * @return ResourcePack[] From 2e58387a437848e68ccbf76062492dbb83c830d6 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 8 Aug 2023 14:55:53 +0100 Subject: [PATCH 12/19] Fixed thread error capture fail in shutdown function the shutdown handler currently isn't called until join(), which sets isKilled to true and stops the error information from being recorded. --- src/thread/CommonThreadPartsTrait.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thread/CommonThreadPartsTrait.php b/src/thread/CommonThreadPartsTrait.php index 340ce554d9..e7429be9bf 100644 --- a/src/thread/CommonThreadPartsTrait.php +++ b/src/thread/CommonThreadPartsTrait.php @@ -124,7 +124,7 @@ trait CommonThreadPartsTrait{ */ protected function onShutdown() : void{ $this->synchronized(function() : void{ - if(!$this->isKilled && $this->crashInfo === null){ + if($this->isTerminated() && $this->crashInfo === null){ $last = error_get_last(); if($last !== null){ //fatal error From 2559d1719f531242a3d010406a4747d74a7239c0 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 8 Aug 2023 14:56:54 +0100 Subject: [PATCH 13/19] All pocketmine\thread\Thread now log uncaught exceptions and fatal errors by default --- src/network/mcpe/raklib/RakLibServer.php | 6 +----- src/scheduler/AsyncWorker.php | 5 ----- src/thread/CommonThreadPartsTrait.php | 16 ++++++++++++++-- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/network/mcpe/raklib/RakLibServer.php b/src/network/mcpe/raklib/RakLibServer.php index e59b6971fe..e2d6d9a669 100644 --- a/src/network/mcpe/raklib/RakLibServer.php +++ b/src/network/mcpe/raklib/RakLibServer.php @@ -85,6 +85,7 @@ class RakLibServer extends Thread{ gc_enable(); ini_set("display_errors", '1'); ini_set("display_startup_errors", '1'); + \GlobalLogger::set($this->logger); $socket = new ServerSocket($this->address->deserialize()); $manager = new Server( @@ -107,11 +108,6 @@ class RakLibServer extends Thread{ $manager->waitShutdown(); } - protected function onUncaughtException(\Throwable $e) : void{ - parent::onUncaughtException($e); - $this->logger->logException($e); - } - public function getThreadName() : string{ return "RakLib"; } diff --git a/src/scheduler/AsyncWorker.php b/src/scheduler/AsyncWorker.php index b26afc29b4..919e3eedcf 100644 --- a/src/scheduler/AsyncWorker.php +++ b/src/scheduler/AsyncWorker.php @@ -69,11 +69,6 @@ class AsyncWorker extends Worker{ $this->saveToThreadStore(self::TLS_KEY_NOTIFIER, $this->sleeperEntry->createNotifier()); } - protected function onUncaughtException(\Throwable $e) : void{ - parent::onUncaughtException($e); - $this->logger->logException($e); - } - public function getLogger() : ThreadSafeLogger{ return $this->logger; } diff --git a/src/thread/CommonThreadPartsTrait.php b/src/thread/CommonThreadPartsTrait.php index e7429be9bf..f5cd8fe0a7 100644 --- a/src/thread/CommonThreadPartsTrait.php +++ b/src/thread/CommonThreadPartsTrait.php @@ -115,6 +115,7 @@ trait CommonThreadPartsTrait{ protected function onUncaughtException(\Throwable $e) : void{ $this->synchronized(function() use ($e) : void{ $this->crashInfo = ThreadCrashInfo::fromThrowable($e, $this->getThreadName()); + \GlobalLogger::get()->logException($e); }); } @@ -128,11 +129,22 @@ trait CommonThreadPartsTrait{ $last = error_get_last(); if($last !== null){ //fatal error - $this->crashInfo = ThreadCrashInfo::fromLastErrorInfo($last, $this->getThreadName()); + $crashInfo = ThreadCrashInfo::fromLastErrorInfo($last, $this->getThreadName()); }else{ //probably misused exit() - $this->crashInfo = ThreadCrashInfo::fromThrowable(new \RuntimeException("Thread crashed without an error - perhaps exit() was called?"), $this->getThreadName()); + $crashInfo = ThreadCrashInfo::fromThrowable(new \RuntimeException("Thread crashed without an error - perhaps exit() was called?"), $this->getThreadName()); } + $this->crashInfo = $crashInfo; + + $lines = []; + //mimic exception printed format + $lines[] = "Fatal error: " . $crashInfo->makePrettyMessage(); + $lines[] = "--- Stack trace ---"; + foreach($crashInfo->getTrace() as $frame){ + $lines[] = " " . $frame->getPrintableFrame(); + } + $lines[] = "--- End of fatal error information ---"; + \GlobalLogger::get()->critical(implode("\n", $lines)); } }); } From 710177ceb5a0a72e9b442331b5b102614ec48335 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 8 Aug 2023 15:10:31 +0100 Subject: [PATCH 14/19] CS this mistake actually hasn't happened for a while --- src/thread/CommonThreadPartsTrait.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/thread/CommonThreadPartsTrait.php b/src/thread/CommonThreadPartsTrait.php index f5cd8fe0a7..de2ecbde81 100644 --- a/src/thread/CommonThreadPartsTrait.php +++ b/src/thread/CommonThreadPartsTrait.php @@ -28,6 +28,7 @@ use pocketmine\errorhandler\ErrorToExceptionHandler; use pocketmine\Server; use function error_get_last; use function error_reporting; +use function implode; use function register_shutdown_function; use function set_exception_handler; From c1638ffaabfb8ffad41e530532f7a131db30a8e8 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 8 Aug 2023 17:08:13 +0100 Subject: [PATCH 15/19] Ban foreach by-reference at the PHPStan level --- phpstan.neon.dist | 1 + src/permission/PermissionManager.php | 6 +-- .../rules/DisallowForeachByReferenceRule.php | 53 +++++++++++++++++++ .../block/regenerate_consistency_check.php | 4 +- 4 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 tests/phpstan/rules/DisallowForeachByReferenceRule.php diff --git a/phpstan.neon.dist b/phpstan.neon.dist index b3aeaf4f6d..1d72511f7c 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -11,6 +11,7 @@ includes: rules: - pocketmine\phpstan\rules\DisallowEnumComparisonRule + - pocketmine\phpstan\rules\DisallowForeachByReferenceRule - pocketmine\phpstan\rules\UnsafeForeachArrayOfStringRule # - pocketmine\phpstan\rules\ThreadedSupportedTypesRule diff --git a/src/permission/PermissionManager.php b/src/permission/PermissionManager.php index b7e622934f..2d83248874 100644 --- a/src/permission/PermissionManager.php +++ b/src/permission/PermissionManager.php @@ -81,9 +81,9 @@ class PermissionManager{ } public function unsubscribeFromAllPermissions(PermissibleInternal $permissible) : void{ - foreach($this->permSubs as $permission => &$subs){ - unset($subs[spl_object_id($permissible)]); - if(count($subs) === 0){ + foreach($this->permSubs as $permission => $subs){ + unset($this->permSubs[$permission][spl_object_id($permissible)]); + if(count($this->permSubs[$permission]) === 0){ unset($this->permSubs[$permission]); } } diff --git a/tests/phpstan/rules/DisallowForeachByReferenceRule.php b/tests/phpstan/rules/DisallowForeachByReferenceRule.php new file mode 100644 index 0000000000..79124d3283 --- /dev/null +++ b/tests/phpstan/rules/DisallowForeachByReferenceRule.php @@ -0,0 +1,53 @@ + + */ +final class DisallowForeachByReferenceRule implements Rule{ + + public function getNodeType() : string{ + return Foreach_::class; + } + + public function processNode(Node $node, Scope $scope) : array{ + /** @var Foreach_ $node */ + if($node->byRef){ + return [ + RuleErrorBuilder::message("Foreach by-reference is not allowed, because it has surprising behaviour.") + ->tip("If the value variable is used outside of the foreach construct (e.g. in a second foreach), the iterable's contents will be unexpectedly altered.") + ->build() + ]; + } + + return []; + } +} diff --git a/tests/phpunit/block/regenerate_consistency_check.php b/tests/phpunit/block/regenerate_consistency_check.php index 9930c54f0c..ac8a1ad9de 100644 --- a/tests/phpunit/block/regenerate_consistency_check.php +++ b/tests/phpunit/block/regenerate_consistency_check.php @@ -24,6 +24,7 @@ declare(strict_types=1); use pocketmine\block\Block; use pocketmine\block\RuntimeBlockStateRegistry; use pocketmine\utils\AssumptionFailedError; +use pocketmine\utils\Utils; require dirname(__DIR__, 3) . '/vendor/autoload.php'; @@ -91,8 +92,9 @@ foreach($new as $stateId => $name){ $newTable[$name][] = $stateId; } ksort($newTable, SORT_STRING); -foreach($newTable as &$stateIds){ +foreach(Utils::stringifyKeys($newTable) as $name => $stateIds){ sort($stateIds, SORT_NUMERIC); + $newTable[$name] = $stateIds; } file_put_contents(__DIR__ . '/block_factory_consistency_check.json', json_encode( From 514fc1ebb5b182c367ac16d3a94bf4594d62d56b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Aug 2023 17:33:16 +0100 Subject: [PATCH 16/19] Bump phpunit/phpunit from 10.2.7 to 10.3.1 (#5965) Bumps [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) from 10.2.7 to 10.3.1. - [Changelog](https://github.com/sebastianbergmann/phpunit/blob/10.3.1/ChangeLog-10.3.md) - [Commits](https://github.com/sebastianbergmann/phpunit/compare/10.2.7...10.3.1) --- updated-dependencies: - dependency-name: phpunit/phpunit dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- composer.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/composer.lock b/composer.lock index 8d65486e77..0e4d424691 100644 --- a/composer.lock +++ b/composer.lock @@ -1861,16 +1861,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.2.7", + "version": "10.3.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "a215d9ee8bac1733796e4ddff3306811f14414e5" + "reference": "d442ce7c4104d5683c12e67e4dcb5058159e9804" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a215d9ee8bac1733796e4ddff3306811f14414e5", - "reference": "a215d9ee8bac1733796e4ddff3306811f14414e5", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d442ce7c4104d5683c12e67e4dcb5058159e9804", + "reference": "d442ce7c4104d5683c12e67e4dcb5058159e9804", "shasum": "" }, "require": { @@ -1910,7 +1910,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.2-dev" + "dev-main": "10.3-dev" } }, "autoload": { @@ -1942,7 +1942,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.2.7" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.3.1" }, "funding": [ { @@ -1958,7 +1958,7 @@ "type": "tidelift" } ], - "time": "2023-08-02T06:46:08+00:00" + "time": "2023-08-04T06:48:08+00:00" }, { "name": "sebastian/cli-parser", From 2c74124e2ea9cd419e65a6495c555cca3b9bc806 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 8 Aug 2023 17:43:01 +0100 Subject: [PATCH 17/19] Update composer dependencies --- composer.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.lock b/composer.lock index 0e4d424691..bdecdb5ed2 100644 --- a/composer.lock +++ b/composer.lock @@ -200,16 +200,16 @@ }, { "name": "pocketmine/bedrock-protocol", - "version": "23.0.2+bedrock-1.20.10", + "version": "23.0.3+bedrock-1.20.10", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "69a309a2dd7dcf3ec8c316385b866397e8c2cbfd" + "reference": "e4157c7af3f91e1b08fe21be171eb73dad7029e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/69a309a2dd7dcf3ec8c316385b866397e8c2cbfd", - "reference": "69a309a2dd7dcf3ec8c316385b866397e8c2cbfd", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/e4157c7af3f91e1b08fe21be171eb73dad7029e9", + "reference": "e4157c7af3f91e1b08fe21be171eb73dad7029e9", "shasum": "" }, "require": { @@ -218,8 +218,8 @@ "php": "^8.0", "pocketmine/binaryutils": "^0.2.0", "pocketmine/color": "^0.2.0 || ^0.3.0", - "pocketmine/math": "^0.3.0 || ^0.4.0", - "pocketmine/nbt": "^0.3.0", + "pocketmine/math": "^0.3.0 || ^0.4.0 || ^1.0.0", + "pocketmine/nbt": "^0.3.0 || ^1.0.0", "ramsey/uuid": "^4.1" }, "require-dev": { @@ -241,9 +241,9 @@ "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", "support": { "issues": "https://github.com/pmmp/BedrockProtocol/issues", - "source": "https://github.com/pmmp/BedrockProtocol/tree/23.0.2+bedrock-1.20.10" + "source": "https://github.com/pmmp/BedrockProtocol/tree/23.0.3+bedrock-1.20.10" }, - "time": "2023-07-24T15:35:36+00:00" + "time": "2023-08-03T15:30:52+00:00" }, { "name": "pocketmine/binaryutils", From 9c1ab943bc8b2bba6179a04390b40adfc867357f Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 8 Aug 2023 17:43:31 +0100 Subject: [PATCH 18/19] Bump ext-pmmpthread min version to 6.0.7 --- composer.json | 2 +- src/PocketMine.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 5cb32d2ac6..75f68f89d6 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "ext-openssl": "*", "ext-pcre": "*", "ext-phar": "*", - "ext-pmmpthread": "^6.0.4", + "ext-pmmpthread": "^6.0.7", "ext-reflection": "*", "ext-simplexml": "*", "ext-sockets": "*", diff --git a/src/PocketMine.php b/src/PocketMine.php index 2c8ab06dee..4c2ff32a73 100644 --- a/src/PocketMine.php +++ b/src/PocketMine.php @@ -120,8 +120,8 @@ namespace pocketmine { } if(($pmmpthread_version = phpversion("pmmpthread")) !== false){ - if(version_compare($pmmpthread_version, "6.0.4") < 0 || version_compare($pmmpthread_version, "7.0.0") >= 0){ - $messages[] = "pmmpthread ^6.0.4 is required, while you have $pmmpthread_version."; + if(version_compare($pmmpthread_version, "6.0.7") < 0 || version_compare($pmmpthread_version, "7.0.0") >= 0){ + $messages[] = "pmmpthread ^6.0.7 is required, while you have $pmmpthread_version."; } } From dd79d4c463a2190d0ae38b0be562595a4deb7e95 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 8 Aug 2023 17:44:13 +0100 Subject: [PATCH 19/19] Updated build/php submodule to pmmp/PHP-Binaries@ed0bc4d2afafd00f9ee92823c6b1bd66789ce4f2 --- build/php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/php b/build/php index 46604f2f6a..ed0bc4d2af 160000 --- a/build/php +++ b/build/php @@ -1 +1 @@ -Subproject commit 46604f2f6a07e3f68a82e4f4d7efdd45629b101e +Subproject commit ed0bc4d2afafd00f9ee92823c6b1bd66789ce4f2