Merge branch 'next-minor' into next-major

This commit is contained in:
Dylan K. Taylor 2022-12-18 21:05:26 +00:00
commit ffa88aff67
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
6 changed files with 118 additions and 45 deletions

View File

@ -37,7 +37,7 @@
"pocketmine/bedrock-block-upgrade-schema": "dev-master@dev", "pocketmine/bedrock-block-upgrade-schema": "dev-master@dev",
"pocketmine/bedrock-data": "dev-modern-world-support@dev", "pocketmine/bedrock-data": "dev-modern-world-support@dev",
"pocketmine/bedrock-item-upgrade-schema": "dev-master", "pocketmine/bedrock-item-upgrade-schema": "dev-master",
"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/binaryutils": "^0.2.1",
"pocketmine/callback-validator": "^1.0.2", "pocketmine/callback-validator": "^1.0.2",
"pocketmine/classloader": "^0.2.0", "pocketmine/classloader": "^0.2.0",

16
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "6c40129ef11a3cfc79081b8f53edda8e", "content-hash": "614023b483badb5ec8720775dd2d3d12",
"packages": [ "packages": [
{ {
"name": "adhocore/json-comment", "name": "adhocore/json-comment",
@ -329,16 +329,16 @@
}, },
{ {
"name": "pocketmine/bedrock-protocol", "name": "pocketmine/bedrock-protocol",
"version": "17.0.0+bedrock-1.19.50", "version": "17.1.0+bedrock-1.19.50",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/pmmp/BedrockProtocol.git", "url": "https://github.com/pmmp/BedrockProtocol.git",
"reference": "272e10197bb1603c0a81075bf5b9dae0d081a6e2" "reference": "c572706cf5e3202718dd35a35dd30fe08cd671de"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/272e10197bb1603c0a81075bf5b9dae0d081a6e2", "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/c572706cf5e3202718dd35a35dd30fe08cd671de",
"reference": "272e10197bb1603c0a81075bf5b9dae0d081a6e2", "reference": "c572706cf5e3202718dd35a35dd30fe08cd671de",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -352,7 +352,7 @@
"ramsey/uuid": "^4.1" "ramsey/uuid": "^4.1"
}, },
"require-dev": { "require-dev": {
"phpstan/phpstan": "1.9.0", "phpstan/phpstan": "1.9.3",
"phpstan/phpstan-phpunit": "^1.0.0", "phpstan/phpstan-phpunit": "^1.0.0",
"phpstan/phpstan-strict-rules": "^1.0.0", "phpstan/phpstan-strict-rules": "^1.0.0",
"phpunit/phpunit": "^9.5" "phpunit/phpunit": "^9.5"
@ -370,9 +370,9 @@
"description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP",
"support": { "support": {
"issues": "https://github.com/pmmp/BedrockProtocol/issues", "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", "name": "pocketmine/binaryutils",

View File

@ -378,7 +378,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
array_values($this->inventory->getContents()), array_values($this->inventory->getContents()),
array_values($this->armorInventory->getContents()), array_values($this->armorInventory->getContents()),
array_values($this->offHandInventory->getContents()), 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{ public function saveNBT() : CompoundTag{

View File

@ -66,6 +66,8 @@ class Item implements \JsonSerializable{
public const TAG_DISPLAY_NAME = "Name"; public const TAG_DISPLAY_NAME = "Name";
public const TAG_DISPLAY_LORE = "Lore"; public const TAG_DISPLAY_LORE = "Lore";
public const TAG_KEEP_ON_DEATH = "minecraft:keep_on_death";
private CompoundTag $nbt; private CompoundTag $nbt;
protected int $count = 1; protected int $count = 1;
@ -89,6 +91,8 @@ class Item implements \JsonSerializable{
*/ */
protected array $canDestroy = []; protected array $canDestroy = [];
protected bool $keepOnDeath = false;
/** /**
* Constructs a new Item type. This constructor should ONLY be used when constructing a new item TYPE to register * Constructs a new Item type. This constructor should ONLY be used when constructing a new item TYPE to register
* into the index. * into the index.
@ -213,6 +217,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. * Returns whether this Item has a non-empty NBT.
*/ */
@ -311,6 +326,8 @@ class Item implements \JsonSerializable{
$this->canDestroy[$entry->getValue()] = $entry->getValue(); $this->canDestroy[$entry->getValue()] = $entry->getValue();
} }
} }
$this->keepOnDeath = $tag->getByte(self::TAG_KEEP_ON_DEATH, 0) !== 0;
} }
protected function serializeCompoundTag(CompoundTag $tag) : void{ protected function serializeCompoundTag(CompoundTag $tag) : void{
@ -368,6 +385,12 @@ class Item implements \JsonSerializable{
}else{ }else{
$tag->removeTag("CanDestroy"); $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{ public function getCount() : int{

View File

@ -131,6 +131,7 @@ use pocketmine\world\sound\Sound;
use pocketmine\world\World; use pocketmine\world\World;
use Ramsey\Uuid\UuidInterface; use Ramsey\Uuid\UuidInterface;
use function abs; use function abs;
use function array_filter;
use function array_map; use function array_map;
use function array_shift; use function array_shift;
use function assert; use function assert;
@ -2280,10 +2281,11 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
$this->getWorld()->dropItem($this->location, $item); $this->getWorld()->dropItem($this->location, $item);
} }
$clearInventory = fn(Inventory $inventory) => $inventory->setContents(array_filter($inventory->getContents(), fn(Item $item) => $item->keepOnDeath()));
$this->inventory->setHeldItemIndex(0); $this->inventory->setHeldItemIndex(0);
$this->inventory->clearAll(); $clearInventory($this->inventory);
$this->armorInventory->clearAll(); $clearInventory($this->armorInventory);
$this->offHandInventory->clearAll(); $clearInventory($this->offHandInventory);
} }
if(!$ev->getKeepXp()){ if(!$ev->getKeepXp()){

View File

@ -38,6 +38,8 @@ use function is_float;
use function is_int; use function is_int;
use function is_string; use function is_string;
use function mkdir; use function mkdir;
use function rtrim;
use function strlen;
use function strtolower; use function strtolower;
use const DIRECTORY_SEPARATOR; use const DIRECTORY_SEPARATOR;
@ -93,25 +95,8 @@ class ResourcePackManager{
} }
$pack = (string) $pack; $pack = (string) $pack;
try{ try{
$packPath = Path::join($this->path, $pack); $newPack = $this->loadPackFromPath(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 = null;
//Detect the type of resource pack.
$info = new \SplFileInfo($packPath);
switch($info->getExtension()){
case "zip":
case "mcpack":
$newPack = new ZippedResourcePack($packPath);
break;
}
if($newPack instanceof ResourcePack){
$this->resourcePacks[] = $newPack; $this->resourcePacks[] = $newPack;
$index = strtolower($newPack->getPackId()); $index = strtolower($newPack->getPackId());
$this->uuidList[$index] = $newPack; $this->uuidList[$index] = $newPack;
@ -119,15 +104,17 @@ class ResourcePackManager{
$keyPath = Path::join($this->path, $pack . ".key"); $keyPath = Path::join($this->path, $pack . ".key");
if(file_exists($keyPath)){ if(file_exists($keyPath)){
try{ try{
$this->encryptionKeys[$index] = ErrorToExceptionHandler::trapAndRemoveFalse( $key = ErrorToExceptionHandler::trapAndRemoveFalse(
fn() => file_get_contents($keyPath) fn() => file_get_contents($keyPath)
); );
}catch(\ErrorException $e){ }catch(\ErrorException $e){
throw new ResourcePackException("Could not read encryption key file: " . $e->getMessage(), 0, $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");
} }
}else{ $this->encryptionKeys[$index] = $key;
throw new ResourcePackException("Format not recognized");
} }
}catch(ResourcePackException $e){ }catch(ResourcePackException $e){
$logger->critical("Could not load resource pack \"$pack\": " . $e->getMessage()); $logger->critical("Could not load resource pack \"$pack\": " . $e->getMessage());
@ -137,6 +124,25 @@ class ResourcePackManager{
$logger->debug("Successfully loaded " . count($this->resourcePacks) . " resource packs"); $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. * Returns the directory which resource packs are loaded from.
*/ */
@ -159,6 +165,29 @@ class ResourcePackManager{
return $this->resourcePacks; 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<ResourcePack> $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. * Returns the resource pack matching the specified UUID string, or null if the ID was not recognized.
*/ */
@ -180,4 +209,23 @@ class ResourcePackManager{
public function getPackEncryptionKey(string $id) : ?string{ public function getPackEncryptionKey(string $id) : ?string{
return $this->encryptionKeys[strtolower($id)] ?? null; 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");
}
}
} }