diff --git a/src/pocketmine/inventory/transaction/CraftingTransaction.php b/src/pocketmine/inventory/transaction/CraftingTransaction.php index e208ae70d..c414d2360 100644 --- a/src/pocketmine/inventory/transaction/CraftingTransaction.php +++ b/src/pocketmine/inventory/transaction/CraftingTransaction.php @@ -32,6 +32,21 @@ use function array_pop; use function count; use function intdiv; +/** + * This transaction type is specialized for crafting validation. It shares most of the same semantics of the base + * inventory transaction type, but the requirement for validity is slightly different. + * + * It is expected that the actions in this transaction type will produce an **unbalanced result**, i.e. some inputs won't + * have corresponding outputs, and vice versa. The reason why is because the unmatched inputs are recipe inputs, and + * the unmatched outputs are recipe results. + * + * Therefore, the validity requirement is that the imbalance of the transaction should match the expected inputs and + * outputs of a registered crafting recipe. + * + * This transaction allows multiple repetitions of the same recipe to be crafted in a single batch. In the case of batch + * crafting, the number of unmatched inputs and outputs must be exactly divisible by the expected recipe ingredients and + * results, with no remainder. Any leftovers are expected to be emitted back to the crafting grid. + */ class CraftingTransaction extends InventoryTransaction{ /** @var CraftingRecipe|null */ protected $recipe; diff --git a/src/pocketmine/inventory/transaction/InventoryTransaction.php b/src/pocketmine/inventory/transaction/InventoryTransaction.php index e74bfa603..d57a9fa1f 100644 --- a/src/pocketmine/inventory/transaction/InventoryTransaction.php +++ b/src/pocketmine/inventory/transaction/InventoryTransaction.php @@ -29,15 +29,29 @@ use pocketmine\inventory\transaction\action\InventoryAction; use pocketmine\inventory\transaction\action\SlotChangeAction; use pocketmine\item\Item; use pocketmine\Player; +use function array_keys; use function assert; use function count; use function get_class; use function min; +use function shuffle; use function spl_object_hash; use function spl_object_id; /** - * This InventoryTransaction only allows doing Transaction between one / two inventories + * This is the basic type for an inventory transaction. This is used for moving items between inventories, dropping + * items and more. It allows transactions with multiple inputs and outputs. + * + * Validation **does not** depend on ordering. This means that the actions can appear in any order and still be valid. + * The only validity requirement for this transaction type is that the balance of items must add up to zero. This means: + * - No new outputs without matching input amounts + * - No inputs without matching output amounts + * - No userdata changes (item state, NBT, etc) + * + * A transaction is composed of "actions", which are pairs of inputs and outputs which target a specific itemstack in + * a specific location. There are multiple types of inventory actions which might be involved in a transaction. + * + * @see InventoryAction */ class InventoryTransaction{ protected $hasExecuted = false; @@ -76,6 +90,11 @@ class InventoryTransaction{ } /** + * Returns an **unordered** set of actions involved in this transaction. + * + * WARNING: This system is **explicitly designed NOT to care about ordering**. Any order seen in this set has NO + * significance and should not be relied on. + * * @return InventoryAction[] */ public function getActions() : array{ @@ -94,6 +113,19 @@ class InventoryTransaction{ } } + /** + * Shuffles actions in the transaction to prevent external things relying on any implicit ordering. + */ + private function shuffleActions() : void{ + $keys = array_keys($this->actions); + shuffle($keys); + $actions = []; + foreach($keys as $key){ + $actions[$key] = $this->actions[$key]; + } + $this->actions = $actions; + } + /** * @internal This method should not be used by plugins, it's used to add tracked inventories for InventoryActions * involving inventories. @@ -272,6 +304,8 @@ class InventoryTransaction{ return false; } + $this->shuffleActions(); + $this->validate(); if(!$this->callExecuteEvent()){