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

Merged
merged 21 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
7b21c85
Add slot validation at inventory level
ShockedPlot7560 Mar 24, 2024
388a769
add some docs
ShockedPlot7560 Mar 24, 2024
69e4db6
Use an object set for the validators instead of an array
ShockedPlot7560 Mar 24, 2024
62f3e47
use Closure and add SlotSafeInventory into BaseInventory by default
ShockedPlot7560 Mar 25, 2024
ca29bb5
All validators need to approve the transaction
ShockedPlot7560 Mar 25, 2024
4637a00
use static closure
ShockedPlot7560 Mar 25, 2024
f3c8576
fix PHPstan
ShockedPlot7560 Mar 25, 2024
37098a4
Merge remote-tracking branch 'upstream/minor-next' into validate-tran…
ShockedPlot7560 Mar 25, 2024
b1b51b4
Many changes
ShockedPlot7560 Mar 29, 2024
80d2edb
use @phpstan-type
ShockedPlot7560 Mar 29, 2024
6b061b6
all validators must validate the transaction to be allowed
ShockedPlot7560 Mar 29, 2024
a87398d
oops
ShockedPlot7560 Mar 29, 2024
ba1b6ea
Apply suggestions from code review
ShockedPlot7560 Apr 1, 2024
2c1ca33
catch TransactionValidationException
ShockedPlot7560 Apr 1, 2024
2d8449e
Use of an interface instead of a wild Closure
ShockedPlot7560 Apr 3, 2024
1d924c3
Apply suggestions from code review
ShockedPlot7560 Apr 4, 2024
99bcbc0
Merge branch 'minor-next' into validate-transaction-slots
ShockedPlot7560 Aug 22, 2024
2a8eb2d
update SlotChangeAction exception message
ShockedPlot7560 Aug 22, 2024
5998bca
change SlotSafeInventory into SlotValidatedInventory
ShockedPlot7560 Aug 22, 2024
e76f03c
Apply suggestions from code review
ShockedPlot7560 Aug 26, 2024
e8adc49
Remove unnecessary condition part (#6439)
ipad54 Aug 29, 2024
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){
dries-c marked this conversation as resolved.
Show resolved Hide resolved
if($item->getArmorSlot() !== $slot){
return new TransactionValidationException("Armor item in wrong slot");
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
}
}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");
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
}
}
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 at slot #" . $this->inventorySlot . ": " . $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.
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
*/
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;
}
Loading