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 3 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
10 changes: 9 additions & 1 deletion src/inventory/ArmorInventory.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,13 @@
namespace pocketmine\inventory;

use pocketmine\entity\Living;
use pocketmine\inventory\transaction\validator\ArmorInventoryValidator;
use pocketmine\item\Item;
use pocketmine\utils\ObjectSet;

class ArmorInventory extends SimpleInventory implements SlotSafeInventory{
use SlotSafeInventoryTrait;

class ArmorInventory extends SimpleInventory{
public const SLOT_HEAD = 0;
public const SLOT_CHEST = 1;
public const SLOT_LEGS = 2;
Expand Down Expand Up @@ -73,4 +77,8 @@
public function setBoots(Item $boots) : void{
$this->setItem(self::SLOT_FEET, $boots);
}

protected static function initSlotValidators(ObjectSet $validators) : void{

Check failure on line 81 in src/inventory/ArmorInventory.php

View workflow job for this annotation

GitHub Actions / PHP 8.1 / PHPStan analysis

Method pocketmine\inventory\ArmorInventory::initSlotValidators() has parameter $validators with generic class pocketmine\utils\ObjectSet but does not specify its types: T

Check failure on line 81 in src/inventory/ArmorInventory.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 / PHPStan analysis

Method pocketmine\inventory\ArmorInventory::initSlotValidators() has parameter $validators with generic class pocketmine\utils\ObjectSet but does not specify its types: T

Check failure on line 81 in src/inventory/ArmorInventory.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 / PHPStan analysis

Method pocketmine\inventory\ArmorInventory::initSlotValidators() has parameter $validators with generic class pocketmine\utils\ObjectSet but does not specify its types: T

Check failure on line 81 in src/inventory/ArmorInventory.php

View workflow job for this annotation

GitHub Actions / PHP 8.1 / PHPStan analysis

Method pocketmine\inventory\ArmorInventory::initSlotValidators() has parameter $validators with generic class pocketmine\utils\ObjectSet but does not specify its types: T

Check failure on line 81 in src/inventory/ArmorInventory.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 / PHPStan analysis

Method pocketmine\inventory\ArmorInventory::initSlotValidators() has parameter $validators with generic class pocketmine\utils\ObjectSet but does not specify its types: T

Check failure on line 81 in src/inventory/ArmorInventory.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 / PHPStan analysis

Method pocketmine\inventory\ArmorInventory::initSlotValidators() has parameter $validators with generic class pocketmine\utils\ObjectSet but does not specify its types: T
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
$validators->add(new ArmorInventoryValidator());
}
}
45 changes: 45 additions & 0 deletions src/inventory/SlotSafeInventory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?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\validator\InventoryTransactionValidator;
use pocketmine\utils\ObjectSet;

/**
* A "slot safe inventory" is an inventory that has a set of rules that determine whether an item can be placed in a
* particular slot. This ensures that the inventory's internal state is always valid and consistent.
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
*/
interface SlotSafeInventory{
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
/**
* Return a set of validators that will be used to determine whether an item can be placed in a particular slot.
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
* All validators need to return true for the transaction to be allowed.
* If one of the validators returns false, the transaction will be cancelled.
*
* There is no guarantee that the validators will be called in any particular order.
* All validators need to be stateless and not depend on the order in which they are called.
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
*
* @phpstan-return ObjectSet<InventoryTransactionValidator>
*/
public static function getSlotValidators() : ObjectSet;
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
}
45 changes: 45 additions & 0 deletions src/inventory/SlotSafeInventoryTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?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\utils\ObjectSet;

trait SlotSafeInventoryTrait{
private static ObjectSet $validators;

/**
* Initialize the set of validators.
* Put here all the internal validators of the inventory.
*/
abstract protected static function initSlotValidators(ObjectSet $validators) : void;

public static function getSlotValidators() : ObjectSet {
if(!isset(self::$validators)){
self::$validators = new ObjectSet();
self::initSlotValidators(self::$validators);
}

return self::$validators;
}
}
13 changes: 13 additions & 0 deletions src/inventory/transaction/action/SlotChangeAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@
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;
use pocketmine\item\VanillaItems;
use pocketmine\player\Player;

/**
Expand Down Expand Up @@ -74,6 +76,17 @@ 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->equals(VanillaItems::AIR())){
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
$result = false;
foreach($this->inventory::getSlotValidators() as $validator){
if(($result = $validator->validate($this->inventory, $this->targetItem, $this->inventorySlot))){
break;
}
}
if(!$result){
throw new TransactionValidationException("Target item is not accepted by the inventory");
}
}
}

/**
Expand Down
44 changes: 44 additions & 0 deletions src/inventory/transaction/validator/ArmorInventoryValidator.php
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\validator;

use pocketmine\block\BlockTypeIds;
use pocketmine\block\MobHead;
use pocketmine\inventory\ArmorInventory;
use pocketmine\inventory\Inventory;
use pocketmine\item\Armor;
use pocketmine\item\Item;
use pocketmine\item\ItemBlock;

class ArmorInventoryValidator implements InventoryTransactionValidator{
public function validate(Inventory $inventory, Item $item, int $slot) : bool{
return $inventory instanceof ArmorInventory && (
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
($item instanceof Armor && $item->getArmorSlot() === $slot) ||
($slot === ArmorInventory::SLOT_HEAD && $item instanceof ItemBlock && (
$item->getBlock()->getTypeId() === BlockTypeIds::CARVED_PUMPKIN ||
$item->getBlock() instanceof MobHead
))
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?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\validator;

use pocketmine\inventory\Inventory;
use pocketmine\inventory\SlotSafeInventory;
use pocketmine\item\Item;

/**
* This interface is used to validate the transactions of a "safe" inventory. {@see SlotSafeInventory}
*
* In this way, each inventory can determine whether or not an item has the right to be placed in a particular slot.
*/
interface InventoryTransactionValidator{
/**
* @return bool Return true, if the item CAN be placed in the given slot of the given inventory.
* Otherwise, return false and the transaction will be cancelled.
*/
public function validate(Inventory $inventory, Item $item, int $slot) : bool;
dktapps marked this conversation as resolved.
Show resolved Hide resolved
}
Loading