Validate transaction slots (#6304)

This commit is contained in:
ShockedPlot7560 2024-09-09 09:48:38 +02:00 committed by GitHub
parent 8cb2e577a1
commit f6e2a1ecce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 170 additions and 1 deletions

View File

@ -23,8 +23,13 @@ declare(strict_types=1);
namespace pocketmine\inventory;
use pocketmine\block\BlockTypeIds;
use pocketmine\entity\Living;
use pocketmine\inventory\transaction\action\validator\CallbackSlotValidator;
use pocketmine\inventory\transaction\TransactionValidationException;
use pocketmine\item\Armor;
use pocketmine\item\Item;
use pocketmine\item\ItemBlock;
class ArmorInventory extends SimpleInventory{
public const SLOT_HEAD = 0;
@ -36,6 +41,8 @@ class ArmorInventory extends SimpleInventory{
protected Living $holder
){
parent::__construct(4);
$this->validators->add(new CallbackSlotValidator($this->validate(...)));
}
public function getHolder() : Living{
@ -73,4 +80,20 @@ class ArmorInventory extends SimpleInventory{
public function setBoots(Item $boots) : void{
$this->setItem(self::SLOT_FEET, $boots);
}
private function validate(Inventory $inventory, Item $item, int $slot) : ?TransactionValidationException{
if($item instanceof Armor){
if($item->getArmorSlot() !== $slot){
return new TransactionValidationException("Armor item is in wrong slot");
}
}else{
if(!($slot === ArmorInventory::SLOT_HEAD && $item instanceof ItemBlock && (
$item->getBlock()->getTypeId() === BlockTypeIds::CARVED_PUMPKIN ||
$item->getBlock()->getTypeId() === BlockTypeIds::MOB_HEAD
))){
return new TransactionValidationException("Item is not accepted in an armor slot");
}
}
return null;
}
}

View File

@ -36,8 +36,10 @@ use function spl_object_id;
/**
* This class provides everything needed to implement an inventory, minus the underlying storage system.
*
* @phpstan-import-type SlotValidators from SlotValidatedInventory
*/
abstract class BaseInventory implements Inventory{
abstract class BaseInventory implements Inventory, SlotValidatedInventory{
protected int $maxStackSize = Inventory::MAX_STACK;
/** @var Player[] */
protected array $viewers = [];
@ -46,9 +48,12 @@ abstract class BaseInventory implements Inventory{
* @phpstan-var ObjectSet<InventoryListener>
*/
protected ObjectSet $listeners;
/** @phpstan-var SlotValidators */
protected ObjectSet $validators;
public function __construct(){
$this->listeners = new ObjectSet();
$this->validators = new ObjectSet();
}
public function getMaxStackSize() : int{
@ -398,4 +403,8 @@ abstract class BaseInventory implements Inventory{
public function getListeners() : ObjectSet{
return $this->listeners;
}
public function getSlotValidators() : ObjectSet{
return $this->validators;
}
}

View File

@ -0,0 +1,46 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\inventory;
use pocketmine\inventory\transaction\action\validator\SlotValidator;
use pocketmine\utils\ObjectSet;
/**
* A "slot validated inventory" has validators which may restrict items
* from being placed in particular slots of the inventory when transactions are executed.
*
* @phpstan-type SlotValidators ObjectSet<SlotValidator>
*/
interface SlotValidatedInventory{
/**
* Returns a set of validators that will be used to determine whether an item can be placed in a particular slot.
* All validators need to return null for the transaction to be allowed.
* If one of the validators returns an exception, the transaction will be cancelled.
*
* There is no guarantee that the validators will be called in any particular order.
*
* @phpstan-return SlotValidators
*/
public function getSlotValidators() : ObjectSet;
}

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\inventory\transaction\action;
use pocketmine\inventory\Inventory;
use pocketmine\inventory\SlotValidatedInventory;
use pocketmine\inventory\transaction\InventoryTransaction;
use pocketmine\inventory\transaction\TransactionValidationException;
use pocketmine\item\Item;
@ -74,6 +75,14 @@ class SlotChangeAction extends InventoryAction{
if($this->targetItem->getCount() > $this->inventory->getMaxStackSize()){
throw new TransactionValidationException("Target item exceeds inventory max stack size");
}
if($this->inventory instanceof SlotValidatedInventory && !$this->targetItem->isNull()){
foreach($this->inventory->getSlotValidators() as $validator){
$ret = $validator->validate($this->inventory, $this->targetItem, $this->inventorySlot);
if($ret !== null){
throw new TransactionValidationException("Target item is not accepted by the inventory at slot #" . $this->inventorySlot . ": " . $ret->getMessage(), 0, $ret);
}
}
}
}
/**

View File

@ -0,0 +1,44 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\inventory\transaction\action\validator;
use pocketmine\inventory\Inventory;
use pocketmine\inventory\transaction\TransactionValidationException;
use pocketmine\item\Item;
use pocketmine\utils\Utils;
class CallbackSlotValidator implements SlotValidator{
/**
* @phpstan-param \Closure(Inventory, Item, int) : ?TransactionValidationException $validate
*/
public function __construct(
private \Closure $validate
){
Utils::validateCallableSignature(function(Inventory $inventory, Item $item, int $slot) : ?TransactionValidationException{ return null; }, $validate);
}
public function validate(Inventory $inventory, Item $item, int $slot) : ?TransactionValidationException{
return ($this->validate)($inventory, $item, $slot);
}
}

View File

@ -0,0 +1,38 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\inventory\transaction\action\validator;
use pocketmine\inventory\Inventory;
use pocketmine\inventory\transaction\TransactionValidationException;
use pocketmine\item\Item;
/**
* Validates a slot placement in an inventory.
*/
interface SlotValidator{
/**
* Returns null if the slot placement is valid, or a TransactionValidationException if it is not.
*/
public function validate(Inventory $inventory, Item $item, int $slot) : ?TransactionValidationException;
}