diff --git a/changelogs/4.18.md b/changelogs/4.18.md index 435784fb0..890f00d6d 100644 --- a/changelogs/4.18.md +++ b/changelogs/4.18.md @@ -97,3 +97,15 @@ Released 29th March 2023. ## Fixes - Fixed players being unable to join due to the appearance of a new `x5t` field in the JWT header of Xbox Live authentication tokens. - Fixed items' durability appearing to reset when moving them around in the inventory. + +# 4.18.3 +Released 5th April 2023. + +## Fixes +- Fixed Average Players not being shown on timings reports when custom player classes are used. +- Fixed incorrect tick violation calculation in timings reports. +- Fixed not being able to add or remove items from the offhand slot. +- Fixed creative inventory item count corruption when taking items (some players would see 64x items in the creative inventory after rejoining or changing gamemode). +- Fixed not being able to drop items directly from the creative inventory on mobile. +- Fixed `DataPacketReceiveEvent` not being called for packets sent by `EntityEventBroadcaster`. +- `CreativeInventory::getItem()` and `CreativeInventory::getAll()` now return cloned itemstacks, to prevent accidental modification of the creative inventory. diff --git a/composer.json b/composer.json index a2f04b810..8cc0dedc2 100644 --- a/composer.json +++ b/composer.json @@ -55,7 +55,7 @@ "symfony/filesystem": "^5.4" }, "require-dev": { - "phpstan/phpstan": "1.10.7", + "phpstan/phpstan": "1.10.11", "phpstan/phpstan-phpunit": "^1.1.0", "phpstan/phpstan-strict-rules": "^1.2.0", "phpunit/phpunit": "^9.2" diff --git a/composer.lock b/composer.lock index 5a2baa2fd..69871ae94 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": "c7ae719a20240005f7c174bedbd67791", + "content-hash": "438d391135646f845eed034c362dffe3", "packages": [ { "name": "adhocore/json-comment", @@ -1775,16 +1775,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.7", + "version": "1.10.11", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "b10ceb526d9607903c5b2673f1fc8775dbe48975" + "reference": "8aa62e6ea8b58ffb650e02940e55a788cbc3fe21" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b10ceb526d9607903c5b2673f1fc8775dbe48975", - "reference": "b10ceb526d9607903c5b2673f1fc8775dbe48975", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/8aa62e6ea8b58ffb650e02940e55a788cbc3fe21", + "reference": "8aa62e6ea8b58ffb650e02940e55a788cbc3fe21", "shasum": "" }, "require": { @@ -1833,7 +1833,7 @@ "type": "tidelift" } ], - "time": "2023-03-16T15:24:20+00:00" + "time": "2023-04-04T19:17:42+00:00" }, { "name": "phpstan/phpstan-phpunit", @@ -1889,16 +1889,16 @@ }, { "name": "phpstan/phpstan-strict-rules", - "version": "1.5.0", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-strict-rules.git", - "reference": "b7dd96a5503919a43b3cd06a2dced9d4252492f2" + "reference": "b21c03d4f6f3a446e4311155f4be9d65048218e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/b7dd96a5503919a43b3cd06a2dced9d4252492f2", - "reference": "b7dd96a5503919a43b3cd06a2dced9d4252492f2", + "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/b21c03d4f6f3a446e4311155f4be9d65048218e6", + "reference": "b21c03d4f6f3a446e4311155f4be9d65048218e6", "shasum": "" }, "require": { @@ -1932,9 +1932,9 @@ "description": "Extra strict and opinionated rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", - "source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.5.0" + "source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.5.1" }, - "time": "2023-02-21T10:17:10+00:00" + "time": "2023-03-29T14:47:40+00:00" }, { "name": "phpunit/php-code-coverage", @@ -2256,16 +2256,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.5", + "version": "9.6.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "86e761949019ae83f49240b2f2123fb5ab3b2fc5" + "reference": "b65d59a059d3004a040c16a82e07bbdf6cfdd115" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/86e761949019ae83f49240b2f2123fb5ab3b2fc5", - "reference": "86e761949019ae83f49240b2f2123fb5ab3b2fc5", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b65d59a059d3004a040c16a82e07bbdf6cfdd115", + "reference": "b65d59a059d3004a040c16a82e07bbdf6cfdd115", "shasum": "" }, "require": { @@ -2338,7 +2338,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.5" + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.6" }, "funding": [ { @@ -2354,7 +2355,7 @@ "type": "tidelift" } ], - "time": "2023-03-09T06:34:10+00:00" + "time": "2023-03-27T11:43:46+00:00" }, { "name": "sebastian/cli-parser", diff --git a/src/inventory/CreativeInventory.php b/src/inventory/CreativeInventory.php index 76cfd874a..d156980f6 100644 --- a/src/inventory/CreativeInventory.php +++ b/src/inventory/CreativeInventory.php @@ -28,6 +28,7 @@ use pocketmine\crafting\json\ItemStackData; use pocketmine\data\bedrock\BedrockDataFiles; use pocketmine\item\Item; use pocketmine\utils\SingletonTrait; +use pocketmine\utils\Utils; final class CreativeInventory{ use SingletonTrait; @@ -62,11 +63,11 @@ final class CreativeInventory{ * @return Item[] */ public function getAll() : array{ - return $this->creative; + return Utils::cloneObjectArray($this->creative); } public function getItem(int $index) : ?Item{ - return $this->creative[$index] ?? null; + return isset($this->creative[$index]) ? clone $this->creative[$index] : null; } public function getItemIndex(Item $item) : int{ diff --git a/src/network/mcpe/NetworkBroadcastUtils.php b/src/network/mcpe/NetworkBroadcastUtils.php index 81d5411ee..76ae98738 100644 --- a/src/network/mcpe/NetworkBroadcastUtils.php +++ b/src/network/mcpe/NetworkBroadcastUtils.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace pocketmine\network\mcpe; -use pocketmine\event\server\DataPacketSendEvent; use pocketmine\network\mcpe\protocol\ClientboundPacket; use pocketmine\player\Player; use pocketmine\timings\Timings; @@ -57,14 +56,6 @@ final class NetworkBroadcastUtils{ return false; } - $ev = new DataPacketSendEvent($sessions, $packets); - $ev->call(); - if($ev->isCancelled()){ - return false; - } - $sessions = $ev->getTargets(); - $packets = $ev->getPackets(); - /** @var PacketBroadcaster[] $uniqueBroadcasters */ $uniqueBroadcasters = []; /** @var NetworkSession[][] $broadcasterTargets */ diff --git a/src/network/mcpe/StandardPacketBroadcaster.php b/src/network/mcpe/StandardPacketBroadcaster.php index 3aa7c535b..9de3d214a 100644 --- a/src/network/mcpe/StandardPacketBroadcaster.php +++ b/src/network/mcpe/StandardPacketBroadcaster.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\network\mcpe; +use pocketmine\event\server\DataPacketSendEvent; use pocketmine\network\mcpe\protocol\serializer\PacketBatch; use pocketmine\network\mcpe\protocol\serializer\PacketSerializer; use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext; @@ -41,6 +42,15 @@ 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; + } + $packets = $ev->getPackets(); + $compressors = []; /** @var NetworkSession[][] $targetsByCompressor */ diff --git a/src/network/mcpe/handler/ItemStackRequestExecutor.php b/src/network/mcpe/handler/ItemStackRequestExecutor.php index 840a23cc1..81d820c2b 100644 --- a/src/network/mcpe/handler/ItemStackRequestExecutor.php +++ b/src/network/mcpe/handler/ItemStackRequestExecutor.php @@ -147,6 +147,11 @@ class ItemStackRequestExecutor{ * @throws ItemStackRequestProcessException */ protected function removeItemFromSlot(ItemStackRequestSlotInfo $slotInfo, int $count) : Item{ + if($slotInfo->getContainerId() === ContainerUIIds::CREATED_OUTPUT && $slotInfo->getSlotId() === UIInventorySlotOffset::CREATED_ITEM_OUTPUT){ + //special case for the "created item" output slot + //TODO: do we need to send a response for this slot info? + return $this->takeCreatedItem($count); + } $this->requestSlotInfos[] = $slotInfo; [$inventory, $slot] = $this->getBuilderInventoryAndSlot($slotInfo); if($count < 1){ @@ -188,6 +193,13 @@ class ItemStackRequestExecutor{ $inventory->setItem($slot, $newItem); } + protected function dropItem(Item $item, int $count) : void{ + if($count < 1){ + throw new ItemStackRequestProcessException("Cannot drop less than 1 of an item"); + } + $this->builder->addAction(new DropItemAction((clone $item)->setCount($count))); + } + /** * @throws ItemStackRequestProcessException */ @@ -254,7 +266,7 @@ class ItemStackRequestExecutor{ /** * @throws ItemStackRequestProcessException */ - protected function takeCreatedItem(ItemStackRequestSlotInfo $destination, int $count) : void{ + protected function takeCreatedItem(int $count) : Item{ if($count < 1){ //this should be impossible at the protocol level, but in case of buggy core code this will prevent exploits throw new ItemStackRequestProcessException("Cannot take less than 1 created item"); @@ -272,10 +284,12 @@ class ItemStackRequestExecutor{ } $this->createdItemsTakenCount += $count; - $this->addItemToSlot($destination, $createdItem, $count); + $createdItem = clone $createdItem; + $createdItem->setCount($count); if(!$this->createdItemFromCreativeInventory && $this->createdItemsTakenCount >= $createdItem->getCount()){ $this->setNextCreatedItem(null); } + return $createdItem; } /** @@ -299,14 +313,7 @@ class ItemStackRequestExecutor{ $action instanceof TakeStackRequestAction || $action instanceof PlaceStackRequestAction ){ - $source = $action->getSource(); - $destination = $action->getDestination(); - - if($source->getContainerId() === ContainerUIIds::CREATED_OUTPUT && $source->getSlotId() === UIInventorySlotOffset::CREATED_ITEM_OUTPUT){ - $this->takeCreatedItem($destination, $action->getCount()); - }else{ - $this->transferItems($source, $destination, $action->getCount()); - } + $this->transferItems($action->getSource(), $action->getDestination(), $action->getCount()); }elseif($action instanceof SwapStackRequestAction){ $this->requestSlotInfos[] = $action->getSlot1(); $this->requestSlotInfos[] = $action->getSlot2(); diff --git a/src/plugin/PluginManager.php b/src/plugin/PluginManager.php index 34a5d816a..bb27e6d2a 100644 --- a/src/plugin/PluginManager.php +++ b/src/plugin/PluginManager.php @@ -63,10 +63,7 @@ use function realpath; use function shuffle; use function sprintf; use function str_contains; -use function str_starts_with; -use function strlen; use function strtolower; -use function substr; /** * Manages all the plugins @@ -654,11 +651,6 @@ class PluginManager{ throw new PluginException("Plugin attempted to register event handler " . $handlerName . "() to event " . $event . " while not enabled"); } - $prefix = $plugin->getDescription()->getSrcNamespacePrefix(); - if(str_starts_with($handlerName, $prefix) && $prefix !== ""){ - $handlerName = substr($handlerName, strlen($prefix) + 1); - } - $timings = new TimingsHandler($handlerName . "(" . (new \ReflectionClass($event))->getShortName() . ")", group: $plugin->getDescription()->getFullName()); $registeredListener = new RegisteredListener($handler, $priority, $plugin, $handleCancelled, $timings); diff --git a/src/timings/Timings.php b/src/timings/Timings.php index 8b739f687..e38af9b46 100644 --- a/src/timings/Timings.php +++ b/src/timings/Timings.php @@ -31,6 +31,7 @@ use pocketmine\network\mcpe\protocol\ServerboundPacket; use pocketmine\player\Player; use pocketmine\scheduler\TaskHandler; use function get_class; +use function str_starts_with; abstract class Timings{ public const INCLUDED_BY_OTHER_TIMINGS_PREFIX = "** "; @@ -279,7 +280,12 @@ abstract class Timings{ public static function getEventTimings(Event $event) : TimingsHandler{ $eventClass = get_class($event); if(!isset(self::$events[$eventClass])){ - self::$events[$eventClass] = new TimingsHandler($eventClass, group: "Events"); + if(str_starts_with($eventClass, "pocketmine\\event\\")){ + $name = (new \ReflectionClass($event))->getShortName(); + }else{ + $name = $eventClass; + } + self::$events[$eventClass] = new TimingsHandler($name, group: "Events"); } return self::$events[$eventClass];