diff --git a/README.md b/README.md index d87a2fc2640..bb0c0c6aefd 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ Additions > https://github.com/pmmp/PocketMine-MP/pull/5122 > https://github.com/pmmp/PocketMine-MP/pull/5232 > https://github.com/pmmp/PocketMine-MP/pull/5276 +> https://github.com/pmmp/PocketMine-MP/pull/5333 ## What is this? diff --git a/src/inventory/transaction/action/DropItemAction.php b/src/inventory/transaction/action/DropItemAction.php index 636317fcf71..bf9039b2479 100644 --- a/src/inventory/transaction/action/DropItemAction.php +++ b/src/inventory/transaction/action/DropItemAction.php @@ -45,6 +45,9 @@ public function validate(Player $source) : void{ if($this->targetItem->getCount() > $this->targetItem->getMaxStackSize()){ throw new TransactionValidationException("Target item exceeds item type max stack size"); } + if($this->targetItem->getLockMode() !== null){ + throw new TransactionValidationException("Target item is locked in inventory or slot"); // todo I don't think this check is necessary. If the origin item is actually locked in a slot, the SlotChangeAction changing it to air will be invalid anyway. + } } public function onPreExecute(Player $source) : bool{ diff --git a/src/inventory/transaction/action/SlotChangeAction.php b/src/inventory/transaction/action/SlotChangeAction.php index 453f0c4d228..f7b3f6687b0 100644 --- a/src/inventory/transaction/action/SlotChangeAction.php +++ b/src/inventory/transaction/action/SlotChangeAction.php @@ -27,6 +27,7 @@ use pocketmine\inventory\transaction\InventoryTransaction; use pocketmine\inventory\transaction\TransactionValidationException; use pocketmine\item\Item; +use pocketmine\item\ItemLockMode; use pocketmine\player\Player; /** @@ -74,6 +75,12 @@ public function validate(Player $source) : void{ if($this->targetItem->getCount() > $this->inventory->getMaxStackSize()){ throw new TransactionValidationException("Target item exceeds inventory max stack size"); } + if($this->targetItem->getLockMode()?->equals(ItemLockMode::SLOT()) === true){ + throw new TransactionValidationException("Target item is locked in slot"); + } + if($this->sourceItem->getLockMode()?->equals(ItemLockMode::SLOT()) === true){ + throw new TransactionValidationException("Source item is locked in slot"); + } } /** diff --git a/src/item/Item.php b/src/item/Item.php index 1a74345b559..ce09c1ac4cf 100644 --- a/src/item/Item.php +++ b/src/item/Item.php @@ -47,6 +47,7 @@ use pocketmine\nbt\tag\StringTag; use pocketmine\nbt\TreeRoot; use pocketmine\player\Player; +use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Utils; use pocketmine\world\format\io\GlobalItemDataHandlers; use function base64_decode; @@ -66,10 +67,14 @@ class Item implements \JsonSerializable{ public const TAG_DISPLAY = "display"; public const TAG_BLOCK_ENTITY_TAG = "BlockEntityTag"; + public const TAG_ITEM_LOCK = "minecraft:item_lock"; public const TAG_DISPLAY_NAME = "Name"; public const TAG_DISPLAY_LORE = "Lore"; + public const VALUE_ITEM_LOCK_IN_SLOT = 1; + public const VALUE_ITEM_LOCK_IN_INVENTORY = 2; + public const TAG_KEEP_ON_DEATH = "minecraft:keep_on_death"; private const TAG_CAN_PLACE_ON = "CanPlaceOn"; //TAG_List @@ -98,6 +103,8 @@ class Item implements \JsonSerializable{ */ protected array $canDestroy = []; + protected ?ItemLockMode $lockMode = null; + protected bool $keepOnDeath = false; /** @@ -227,6 +234,15 @@ public function setCanDestroy(array $canDestroy) : void{ } } + public function getLockMode() : ?ItemLockMode{ + return $this->lockMode; + } + + public function setLockMode(?ItemLockMode $lockMode) : self { + $this->lockMode = $lockMode; + return $this; + } + /** * Returns whether players will retain this item on death. If a non-player dies it will be excluded from the drops. */ @@ -336,6 +352,11 @@ protected function deserializeCompoundTag(CompoundTag $tag) : void{ $this->canDestroy[$entry->getValue()] = $entry->getValue(); } } + $this->lockMode = match ($tag->getByte(self::TAG_ITEM_LOCK, 0)) { + self::VALUE_ITEM_LOCK_IN_SLOT => ItemLockMode::SLOT(), + self::VALUE_ITEM_LOCK_IN_INVENTORY => ItemLockMode::INVENTORY(), + default => null + }; $this->keepOnDeath = $tag->getByte(self::TAG_KEEP_ON_DEATH, 0) !== 0; } @@ -401,6 +422,16 @@ protected function serializeCompoundTag(CompoundTag $tag) : void{ $tag->removeTag(self::TAG_CAN_DESTROY); } + if ($this->lockMode !== null) { + $tag->setByte(self::TAG_ITEM_LOCK, match ($this->lockMode->id()) { + ItemLockMode::SLOT()->id() => self::VALUE_ITEM_LOCK_IN_SLOT, + ItemLockMode::INVENTORY()->id() => self::VALUE_ITEM_LOCK_IN_INVENTORY, + default => throw new AssumptionFailedError("Unknown lock mode") + }); + } else { + $tag->removeTag(self::TAG_ITEM_LOCK); + } + if($this->keepOnDeath){ $tag->setByte(self::TAG_KEEP_ON_DEATH, 1); }else{ diff --git a/src/item/ItemLockMode.php b/src/item/ItemLockMode.php new file mode 100644 index 00000000000..991be29011d --- /dev/null +++ b/src/item/ItemLockMode.php @@ -0,0 +1,25 @@ +blockCache[$chunkHash][$relativeBlockHash] = $block; + $this->blockCache[$chunkHash][$relativeBlockHash] = $block; // todo add fix for https://github.com/pmmp/PocketMine-MP/issues/152 by adding cache size } return $block;