From c5d716dc9dd18a82e1c0e572b065872b6b7a6742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Le=C3=B3n?= <58715544+JavierLeon9966@users.noreply.github.com> Date: Thu, 15 Dec 2022 17:10:20 -0300 Subject: [PATCH 1/6] Added keep on death methods for items (#5395) --- src/entity/Human.php | 2 +- src/item/Item.php | 23 +++++++++++++++++++++++ src/player/Player.php | 8 +++++--- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/entity/Human.php b/src/entity/Human.php index ade87f981..9123bad87 100644 --- a/src/entity/Human.php +++ b/src/entity/Human.php @@ -391,7 +391,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ $this->inventory !== null ? array_values($this->inventory->getContents()) : [], $this->armorInventory !== null ? array_values($this->armorInventory->getContents()) : [], $this->offHandInventory !== null ? array_values($this->offHandInventory->getContents()) : [], - ), function(Item $item) : bool{ return !$item->hasEnchantment(VanillaEnchantments::VANISHING()); }); + ), function(Item $item) : bool{ return !$item->hasEnchantment(VanillaEnchantments::VANISHING()) && !$item->keepOnDeath(); }); } public function saveNBT() : CompoundTag{ diff --git a/src/item/Item.php b/src/item/Item.php index f285a5287..3f0d0b643 100644 --- a/src/item/Item.php +++ b/src/item/Item.php @@ -65,6 +65,8 @@ class Item implements \JsonSerializable{ public const TAG_DISPLAY_NAME = "Name"; public const TAG_DISPLAY_LORE = "Lore"; + public const TAG_KEEP_ON_DEATH = "minecraft:keep_on_death"; + private ItemIdentifier $identifier; private CompoundTag $nbt; @@ -96,6 +98,8 @@ class Item implements \JsonSerializable{ */ protected $canDestroy; + protected bool $keepOnDeath = false; + /** * Constructs a new Item type. This constructor should ONLY be used when constructing a new item TYPE to register * into the index. @@ -222,6 +226,17 @@ class Item implements \JsonSerializable{ } } + /** + * Returns whether players will retain this item on death. If a non-player dies it will be excluded from the drops. + */ + public function keepOnDeath() : bool{ + return $this->keepOnDeath; + } + + public function setKeepOnDeath(bool $keepOnDeath) : void{ + $this->keepOnDeath = $keepOnDeath; + } + /** * Returns whether this Item has a non-empty NBT. */ @@ -320,6 +335,8 @@ class Item implements \JsonSerializable{ $this->canDestroy[$entry->getValue()] = $entry->getValue(); } } + + $this->keepOnDeath = $tag->getByte(self::TAG_KEEP_ON_DEATH, 0) !== 0; } protected function serializeCompoundTag(CompoundTag $tag) : void{ @@ -377,6 +394,12 @@ class Item implements \JsonSerializable{ }else{ $tag->removeTag("CanDestroy"); } + + if($this->keepOnDeath){ + $tag->setByte(self::TAG_KEEP_ON_DEATH, 1); + }else{ + $tag->removeTag(self::TAG_KEEP_ON_DEATH); + } } public function getCount() : int{ diff --git a/src/player/Player.php b/src/player/Player.php index 32e3597a8..5d7215012 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -131,6 +131,7 @@ use pocketmine\world\sound\Sound; use pocketmine\world\World; use Ramsey\Uuid\UuidInterface; use function abs; +use function array_filter; use function array_map; use function assert; use function count; @@ -2265,15 +2266,16 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $this->getWorld()->dropItem($this->location, $item); } + $clearInventory = fn(Inventory $inventory) => $inventory->setContents(array_filter($inventory->getContents(), fn(Item $item) => $item->keepOnDeath())); if($this->inventory !== null){ $this->inventory->setHeldItemIndex(0); - $this->inventory->clearAll(); + $clearInventory($this->inventory); } if($this->armorInventory !== null){ - $this->armorInventory->clearAll(); + $clearInventory($this->armorInventory); } if($this->offHandInventory !== null){ - $this->offHandInventory->clearAll(); + $clearInventory($this->offHandInventory); } } From 50b70708fb238b10462064ff70ba429046c87431 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 15 Dec 2022 21:59:42 +0000 Subject: [PATCH 2/6] ResourcePackManager: extracted loadPackFromPath() private method from constructor body --- src/resourcepacks/ResourcePackManager.php | 63 +++++++++++------------ 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/src/resourcepacks/ResourcePackManager.php b/src/resourcepacks/ResourcePackManager.php index c8b2cb389..07873030c 100644 --- a/src/resourcepacks/ResourcePackManager.php +++ b/src/resourcepacks/ResourcePackManager.php @@ -93,41 +93,21 @@ class ResourcePackManager{ } $pack = (string) $pack; try{ - $packPath = Path::join($this->path, $pack); - if(!file_exists($packPath)){ - throw new ResourcePackException("File or directory not found"); - } - if(is_dir($packPath)){ - throw new ResourcePackException("Directory resource packs are unsupported"); - } + $newPack = $this->loadPackFromPath(Path::join($this->path, $pack)); - $newPack = null; - //Detect the type of resource pack. - $info = new \SplFileInfo($packPath); - switch($info->getExtension()){ - case "zip": - case "mcpack": - $newPack = new ZippedResourcePack($packPath); - break; - } + $this->resourcePacks[] = $newPack; + $index = strtolower($newPack->getPackId()); + $this->uuidList[$index] = $newPack; - if($newPack instanceof ResourcePack){ - $this->resourcePacks[] = $newPack; - $index = strtolower($newPack->getPackId()); - $this->uuidList[$index] = $newPack; - - $keyPath = Path::join($this->path, $pack . ".key"); - if(file_exists($keyPath)){ - try{ - $this->encryptionKeys[$index] = ErrorToExceptionHandler::trapAndRemoveFalse( - fn() => file_get_contents($keyPath) - ); - }catch(\ErrorException $e){ - throw new ResourcePackException("Could not read encryption key file: " . $e->getMessage(), 0, $e); - } + $keyPath = Path::join($this->path, $pack . ".key"); + if(file_exists($keyPath)){ + try{ + $this->encryptionKeys[$index] = ErrorToExceptionHandler::trapAndRemoveFalse( + fn() => file_get_contents($keyPath) + ); + }catch(\ErrorException $e){ + throw new ResourcePackException("Could not read encryption key file: " . $e->getMessage(), 0, $e); } - }else{ - throw new ResourcePackException("Format not recognized"); } }catch(ResourcePackException $e){ $logger->critical("Could not load resource pack \"$pack\": " . $e->getMessage()); @@ -137,6 +117,25 @@ class ResourcePackManager{ $logger->debug("Successfully loaded " . count($this->resourcePacks) . " resource packs"); } + private function loadPackFromPath(string $packPath) : ResourcePack{ + if(!file_exists($packPath)){ + throw new ResourcePackException("File or directory not found"); + } + if(is_dir($packPath)){ + throw new ResourcePackException("Directory resource packs are unsupported"); + } + + //Detect the type of resource pack. + $info = new \SplFileInfo($packPath); + switch($info->getExtension()){ + case "zip": + case "mcpack": + return new ZippedResourcePack($packPath); + } + + throw new ResourcePackException("Format not recognized"); + } + /** * Returns the directory which resource packs are loaded from. */ From 880d01daea239fae895dbc2582b8df1d1aa96f28 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 15 Dec 2022 22:10:53 +0000 Subject: [PATCH 3/6] ResourcePackManager: added setResourceStack() this enables plugins to modify the resource pack stack however they see fit. Modifying the existing stack can be done by doing array modifications on the result of getResourceStack() and then setting it back again using this method. --- src/resourcepacks/ResourcePackManager.php | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/resourcepacks/ResourcePackManager.php b/src/resourcepacks/ResourcePackManager.php index 07873030c..cd5d3a2f7 100644 --- a/src/resourcepacks/ResourcePackManager.php +++ b/src/resourcepacks/ResourcePackManager.php @@ -158,6 +158,29 @@ class ResourcePackManager{ return $this->resourcePacks; } + /** + * Sets the resource packs to use. Packs earliest in the list will appear at the top of the stack (maximum + * priority), and later ones will appear below (lower priority), in the same manner as the Bedrock resource packs + * screen in-game. + * + * @param ResourcePack[] $resourceStack + * @phpstan-param list $resourceStack + */ + public function setResourceStack(array $resourceStack) : void{ + $uuidList = []; + $resourcePacks = []; + foreach($resourceStack as $pack){ + $uuid = strtolower($pack->getPackId()); + if(isset($uuidList[$uuid])){ + throw new \InvalidArgumentException("Cannot load two resource pack with the same UUID ($uuid)"); + } + $uuidList[$uuid] = $pack; + $resourcePacks[] = $pack; + } + $this->resourcePacks = $resourcePacks; + $this->uuidList = $uuidList; + } + /** * Returns the resource pack matching the specified UUID string, or null if the ID was not recognized. */ From d02c6668b249b145cf9caed3aada560b140e8c19 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 15 Dec 2022 22:29:34 +0000 Subject: [PATCH 4/6] ResourcePackManager: added setPackEncryptionKey() this allows plugins to install their own encrypted resource packs --- src/resourcepacks/ResourcePackManager.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/resourcepacks/ResourcePackManager.php b/src/resourcepacks/ResourcePackManager.php index cd5d3a2f7..6d71a1f9f 100644 --- a/src/resourcepacks/ResourcePackManager.php +++ b/src/resourcepacks/ResourcePackManager.php @@ -38,6 +38,7 @@ use function is_float; use function is_int; use function is_string; use function mkdir; +use function strlen; use function strtolower; use const DIRECTORY_SEPARATOR; @@ -202,4 +203,23 @@ class ResourcePackManager{ public function getPackEncryptionKey(string $id) : ?string{ return $this->encryptionKeys[strtolower($id)] ?? null; } + + /** + * Sets the encryption key to use for decrypting the specified resource pack. The pack will **NOT** be decrypted by + * PocketMine-MP; the key is simply passed to the client to allow it to decrypt the pack after downloading it. + */ + public function setPackEncryptionKey(string $id, ?string $key) : void{ + $id = strtolower($id); + if($key === null){ + //allow deprovisioning keys for resource packs that have been removed + unset($this->encryptionKeys[$id]); + }elseif(isset($this->uuidList[$id])){ + if(strlen($key) !== 32){ + throw new \InvalidArgumentException("Encryption key must be exactly 32 bytes long"); + } + $this->encryptionKeys[$id] = $key; + }else{ + throw new \InvalidArgumentException("Unknown pack ID $id"); + } + } } From 8c0d3943d8d9346a689bb2a09739c6a8d54e1ac9 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 15 Dec 2022 22:36:06 +0000 Subject: [PATCH 5/6] Added length validation for resource pack encryption keys --- src/resourcepacks/ResourcePackManager.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/resourcepacks/ResourcePackManager.php b/src/resourcepacks/ResourcePackManager.php index 6d71a1f9f..655f3a3a9 100644 --- a/src/resourcepacks/ResourcePackManager.php +++ b/src/resourcepacks/ResourcePackManager.php @@ -38,6 +38,7 @@ use function is_float; use function is_int; use function is_string; use function mkdir; +use function rtrim; use function strlen; use function strtolower; use const DIRECTORY_SEPARATOR; @@ -103,12 +104,17 @@ class ResourcePackManager{ $keyPath = Path::join($this->path, $pack . ".key"); if(file_exists($keyPath)){ try{ - $this->encryptionKeys[$index] = ErrorToExceptionHandler::trapAndRemoveFalse( + $key = ErrorToExceptionHandler::trapAndRemoveFalse( fn() => file_get_contents($keyPath) ); }catch(\ErrorException $e){ throw new ResourcePackException("Could not read encryption key file: " . $e->getMessage(), 0, $e); } + $key = rtrim($key, "\r\n"); + if(strlen($key) !== 32){ + throw new ResourcePackException("Invalid encryption key length, must be exactly 32 bytes"); + } + $this->encryptionKeys[$index] = $key; } }catch(ResourcePackException $e){ $logger->critical("Could not load resource pack \"$pack\": " . $e->getMessage()); From aa374083d8b87a17df372b3010d2268dba4f3df8 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 18 Dec 2022 21:00:52 +0000 Subject: [PATCH 6/6] Accept new BedrockProtocol version --- composer.json | 2 +- composer.lock | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/composer.json b/composer.json index 3b31d00c7..99ba8585d 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,7 @@ "fgrosse/phpasn1": "^2.3", "netresearch/jsonmapper": "^4.0", "pocketmine/bedrock-data": "~1.13.0+bedrock-1.19.50", - "pocketmine/bedrock-protocol": "~17.0.0+bedrock-1.19.50", + "pocketmine/bedrock-protocol": "~17.1.0+bedrock-1.19.50", "pocketmine/binaryutils": "^0.2.1", "pocketmine/callback-validator": "^1.0.2", "pocketmine/classloader": "^0.2.0", diff --git a/composer.lock b/composer.lock index faeee4ffc..9f86c54ed 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": "6818c4f3d55e45758df83a56d60653cf", + "content-hash": "015abb4a8b3ec678578346d1e06c477a", "packages": [ { "name": "adhocore/json-comment", @@ -275,16 +275,16 @@ }, { "name": "pocketmine/bedrock-protocol", - "version": "17.0.0+bedrock-1.19.50", + "version": "17.1.0+bedrock-1.19.50", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "272e10197bb1603c0a81075bf5b9dae0d081a6e2" + "reference": "c572706cf5e3202718dd35a35dd30fe08cd671de" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/272e10197bb1603c0a81075bf5b9dae0d081a6e2", - "reference": "272e10197bb1603c0a81075bf5b9dae0d081a6e2", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/c572706cf5e3202718dd35a35dd30fe08cd671de", + "reference": "c572706cf5e3202718dd35a35dd30fe08cd671de", "shasum": "" }, "require": { @@ -298,7 +298,7 @@ "ramsey/uuid": "^4.1" }, "require-dev": { - "phpstan/phpstan": "1.9.0", + "phpstan/phpstan": "1.9.3", "phpstan/phpstan-phpunit": "^1.0.0", "phpstan/phpstan-strict-rules": "^1.0.0", "phpunit/phpunit": "^9.5" @@ -316,9 +316,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/17.0.0+bedrock-1.19.50" + "source": "https://github.com/pmmp/BedrockProtocol/tree/17.1.0+bedrock-1.19.50" }, - "time": "2022-11-30T16:16:27+00:00" + "time": "2022-12-15T20:34:49+00:00" }, { "name": "pocketmine/binaryutils",