Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validate transaction slots #6304

Open
wants to merge 16 commits into
base: minor-next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/inventory/ArmorInventory.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,13 @@

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;
Expand All @@ -36,6 +41,8 @@ public function __construct(
protected Living $holder
){
parent::__construct(4);

$this->validators->add(new CallbackSlotValidator($this->validate(...)));
}

public function getHolder() : Living{
Expand Down Expand Up @@ -73,4 +80,20 @@ public function setLeggings(Item $leggings) : void{
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 && $item->getArmorSlot() === $slot){
if($item->getArmorSlot() !== $slot){
return new TransactionValidationException("Armor item 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 not accepted in an armor slot");
}
}
return null;
}
}
11 changes: 10 additions & 1 deletion src/inventory/BaseInventory.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@

/**
* This class provides everything needed to implement an inventory, minus the underlying storage system.
*
* @phpstan-import-type SlotValidators from SlotSafeInventory
*/
abstract class BaseInventory implements Inventory{
abstract class BaseInventory implements Inventory, SlotSafeInventory{
protected int $maxStackSize = Inventory::MAX_STACK;
/** @var Player[] */
protected array $viewers = [];
Expand All @@ -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{
Expand Down Expand Up @@ -398,4 +403,8 @@ public function slotExists(int $slot) : bool{
public function getListeners() : ObjectSet{
return $this->listeners;
}

public function getSlotValidators() : ObjectSet{
return $this->validators;
}
}
46 changes: 46 additions & 0 deletions src/inventory/SlotSafeInventory.php
Original file line number Diff line number Diff line change
@@ -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 safe inventory" has validators which may restrict items
* from being placed in particular slots of the inventory.
*
* @phpstan-type SlotValidators ObjectSet<SlotValidator>
*/
interface SlotSafeInventory{
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
/**
* 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;
}
9 changes: 9 additions & 0 deletions src/inventory/transaction/action/SlotChangeAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
namespace pocketmine\inventory\transaction\action;

use pocketmine\inventory\Inventory;
use pocketmine\inventory\SlotSafeInventory;
use pocketmine\inventory\transaction\InventoryTransaction;
use pocketmine\inventory\transaction\TransactionValidationException;
use pocketmine\item\Item;
Expand Down Expand Up @@ -74,6 +75,14 @@ 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->inventory instanceof SlotSafeInventory && !$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 slot: " . $ret->getMessage(), 0, $ret);
}
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
38 changes: 38 additions & 0 deletions src/inventory/transaction/action/validator/SlotValidator.php
Original file line number Diff line number Diff line change
@@ -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;

/**
* Validate 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;
}