Skip to content

Commit c4da02f

Browse files
committed
Validate transaction slots (pmmp#6304)
1 parent d251b85 commit c4da02f

File tree

6 files changed

+166
-1
lines changed

6 files changed

+166
-1
lines changed

src/inventory/ArmorInventory.php

+23
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,13 @@
2323

2424
namespace pocketmine\inventory;
2525

26+
use pocketmine\block\BlockTypeIds;
2627
use pocketmine\entity\Living;
28+
use pocketmine\inventory\transaction\action\validator\CallbackSlotValidator;
29+
use pocketmine\inventory\transaction\TransactionValidationException;
30+
use pocketmine\item\Armor;
2731
use pocketmine\item\Item;
32+
use pocketmine\item\ItemBlock;
2833

2934
class ArmorInventory extends SimpleInventory{
3035
public const SLOT_HEAD = 0;
@@ -36,6 +41,8 @@ public function __construct(
3641
protected Living $holder
3742
){
3843
parent::__construct(4);
44+
45+
$this->validators->add(new CallbackSlotValidator($this->validate(...)));
3946
}
4047

4148
public function getHolder() : Living{
@@ -73,4 +80,20 @@ public function setLeggings(Item $leggings) : void{
7380
public function setBoots(Item $boots) : void{
7481
$this->setItem(self::SLOT_FEET, $boots);
7582
}
83+
84+
private function validate(Inventory $inventory, Item $item, int $slot) : ?TransactionValidationException{
85+
if($item instanceof Armor){
86+
if($item->getArmorSlot() !== $slot){
87+
return new TransactionValidationException("Armor item is in wrong slot");
88+
}
89+
}else{
90+
if(!($slot === ArmorInventory::SLOT_HEAD && $item instanceof ItemBlock && (
91+
$item->getBlock()->getTypeId() === BlockTypeIds::CARVED_PUMPKIN ||
92+
$item->getBlock()->getTypeId() === BlockTypeIds::MOB_HEAD
93+
))){
94+
return new TransactionValidationException("Item is not accepted in an armor slot");
95+
}
96+
}
97+
return null;
98+
}
7699
}

src/inventory/BaseInventory.php

+10-1
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,10 @@
3737

3838
/**
3939
* This class provides everything needed to implement an inventory, minus the underlying storage system.
40+
*
41+
* @phpstan-import-type SlotValidators from SlotValidatedInventory
4042
*/
41-
abstract class BaseInventory implements Inventory, ProximityRestricted{
43+
abstract class BaseInventory implements Inventory, ProximityRestricted, SlotValidatedInventory{
4244
protected int $maxStackSize = Inventory::MAX_STACK;
4345

4446
protected int $maxDistanceFromContainer = ProximityRestricted::MAX_DISTANCE;
@@ -50,9 +52,12 @@ abstract class BaseInventory implements Inventory, ProximityRestricted{
5052
* @phpstan-var ObjectSet<InventoryListener>
5153
*/
5254
protected ObjectSet $listeners;
55+
/** @phpstan-var SlotValidators */
56+
protected ObjectSet $validators;
5357

5458
public function __construct(){
5559
$this->listeners = new ObjectSet();
60+
$this->validators = new ObjectSet();
5661
}
5762

5863
public function getMaxStackSize() : int{
@@ -410,4 +415,8 @@ public function slotExists(int $slot) : bool{
410415
public function getListeners() : ObjectSet{
411416
return $this->listeners;
412417
}
418+
419+
public function getSlotValidators() : ObjectSet{
420+
return $this->validators;
421+
}
413422
}
+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
/*
3+
*
4+
* ____ _ _ __ __ _ __ __ ____
5+
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
6+
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
7+
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
8+
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
9+
*
10+
* This program is free software: you can redistribute it and/or modify
11+
* it under the terms of the GNU Lesser General Public License as published by
12+
* the Free Software Foundation, either version 3 of the License, or
13+
* (at your option) any later version.
14+
*
15+
* @author PocketMine Team
16+
* @link http://www.pocketmine.net/
17+
*
18+
*
19+
*/
20+
21+
declare(strict_types=1);
22+
23+
namespace pocketmine\inventory;
24+
25+
use pocketmine\inventory\transaction\action\validator\SlotValidator;
26+
use pocketmine\utils\ObjectSet;
27+
28+
/**
29+
* A "slot validated inventory" has validators which may restrict items
30+
* from being placed in particular slots of the inventory when transactions are executed.
31+
*
32+
* @phpstan-type SlotValidators ObjectSet<SlotValidator>
33+
*/
34+
interface SlotValidatedInventory{
35+
/**
36+
* Returns a set of validators that will be used to determine whether an item can be placed in a particular slot.
37+
* All validators need to return null for the transaction to be allowed.
38+
* If one of the validators returns an exception, the transaction will be cancelled.
39+
*
40+
* There is no guarantee that the validators will be called in any particular order.
41+
*
42+
* @phpstan-return SlotValidators
43+
*/
44+
public function getSlotValidators() : ObjectSet;
45+
}

src/inventory/transaction/action/SlotChangeAction.php

+9
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
namespace pocketmine\inventory\transaction\action;
2525

2626
use pocketmine\inventory\Inventory;
27+
use pocketmine\inventory\SlotValidatedInventory;
2728
use pocketmine\inventory\transaction\InventoryTransaction;
2829
use pocketmine\inventory\transaction\TransactionValidationException;
2930
use pocketmine\item\Item;
@@ -75,6 +76,14 @@ public function validate(Player $source) : void{
7576
if($this->targetItem->getCount() > $this->inventory->getMaxStackSize()){
7677
throw new TransactionValidationException("Target item exceeds inventory max stack size");
7778
}
79+
if($this->inventory instanceof SlotValidatedInventory && !$this->targetItem->isNull()){
80+
foreach($this->inventory->getSlotValidators() as $validator){
81+
$ret = $validator->validate($this->inventory, $this->targetItem, $this->inventorySlot);
82+
if($ret !== null){
83+
throw new TransactionValidationException("Target item is not accepted by the inventory at slot #" . $this->inventorySlot . ": " . $ret->getMessage(), 0, $ret);
84+
}
85+
}
86+
}
7887
if($this->targetItem->getLockMode()?->equals(ItemLockMode::SLOT()) === true){
7988
throw new TransactionValidationException("Target item is locked in slot");
8089
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
/*
3+
*
4+
* ____ _ _ __ __ _ __ __ ____
5+
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
6+
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
7+
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
8+
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
9+
*
10+
* This program is free software: you can redistribute it and/or modify
11+
* it under the terms of the GNU Lesser General Public License as published by
12+
* the Free Software Foundation, either version 3 of the License, or
13+
* (at your option) any later version.
14+
*
15+
* @author PocketMine Team
16+
* @link http://www.pocketmine.net/
17+
*
18+
*
19+
*/
20+
declare(strict_types=1);
21+
22+
namespace pocketmine\inventory\transaction\action\validator;
23+
24+
use pocketmine\inventory\Inventory;
25+
use pocketmine\inventory\transaction\TransactionValidationException;
26+
use pocketmine\item\Item;
27+
use pocketmine\utils\Utils;
28+
29+
class CallbackSlotValidator implements SlotValidator{
30+
/**
31+
* @phpstan-param \Closure(Inventory, Item, int) : ?TransactionValidationException $validate
32+
*/
33+
public function __construct(
34+
private \Closure $validate
35+
){
36+
Utils::validateCallableSignature(function(Inventory $inventory, Item $item, int $slot) : ?TransactionValidationException{ return null; }, $validate);
37+
}
38+
39+
public function validate(Inventory $inventory, Item $item, int $slot) : ?TransactionValidationException{
40+
return ($this->validate)($inventory, $item, $slot);
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
/*
3+
*
4+
* ____ _ _ __ __ _ __ __ ____
5+
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
6+
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
7+
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
8+
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
9+
*
10+
* This program is free software: you can redistribute it and/or modify
11+
* it under the terms of the GNU Lesser General Public License as published by
12+
* the Free Software Foundation, either version 3 of the License, or
13+
* (at your option) any later version.
14+
*
15+
* @author PocketMine Team
16+
* @link http://www.pocketmine.net/
17+
*
18+
*
19+
*/
20+
21+
declare(strict_types=1);
22+
23+
namespace pocketmine\inventory\transaction\action\validator;
24+
25+
use pocketmine\inventory\Inventory;
26+
use pocketmine\inventory\transaction\TransactionValidationException;
27+
use pocketmine\item\Item;
28+
29+
/**
30+
* Validates a slot placement in an inventory.
31+
*/
32+
interface SlotValidator{
33+
/**
34+
* Returns null if the slot placement is valid, or a TransactionValidationException if it is not.
35+
*/
36+
public function validate(Inventory $inventory, Item $item, int $slot) : ?TransactionValidationException;
37+
}

0 commit comments

Comments
 (0)