mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-07 10:22:56 +00:00
Removed pocketmine subdirectory, map PSR-4 style
This commit is contained in:
172
src/inventory/transaction/CraftingTransaction.php
Normal file
172
src/inventory/transaction/CraftingTransaction.php
Normal file
@ -0,0 +1,172 @@
|
||||
<?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;
|
||||
|
||||
use pocketmine\crafting\CraftingRecipe;
|
||||
use pocketmine\event\inventory\CraftItemEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\network\mcpe\protocol\ContainerClosePacket;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ContainerIds;
|
||||
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;
|
||||
/** @var int|null */
|
||||
protected $repetitions;
|
||||
|
||||
/** @var Item[] */
|
||||
protected $inputs = [];
|
||||
/** @var Item[] */
|
||||
protected $outputs = [];
|
||||
|
||||
/**
|
||||
* @param Item[] $txItems
|
||||
* @param Item[] $recipeItems
|
||||
* @param bool $wildcards
|
||||
* @param int $iterations
|
||||
*
|
||||
* @return int
|
||||
* @throws TransactionValidationException
|
||||
*/
|
||||
protected function matchRecipeItems(array $txItems, array $recipeItems, bool $wildcards, int $iterations = 0) : int{
|
||||
if(empty($recipeItems)){
|
||||
throw new TransactionValidationException("No recipe items given");
|
||||
}
|
||||
if(empty($txItems)){
|
||||
throw new TransactionValidationException("No transaction items given");
|
||||
}
|
||||
|
||||
while(!empty($recipeItems)){
|
||||
/** @var Item $recipeItem */
|
||||
$recipeItem = array_pop($recipeItems);
|
||||
$needCount = $recipeItem->getCount();
|
||||
foreach($recipeItems as $i => $otherRecipeItem){
|
||||
if($otherRecipeItem->equals($recipeItem)){ //make sure they have the same wildcards set
|
||||
$needCount += $otherRecipeItem->getCount();
|
||||
unset($recipeItems[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
$haveCount = 0;
|
||||
foreach($txItems as $j => $txItem){
|
||||
if($txItem->equals($recipeItem, !$wildcards or !$recipeItem->hasAnyDamageValue(), !$wildcards or $recipeItem->hasNamedTag())){
|
||||
$haveCount += $txItem->getCount();
|
||||
unset($txItems[$j]);
|
||||
}
|
||||
}
|
||||
|
||||
if($haveCount % $needCount !== 0){
|
||||
//wrong count for this output, should divide exactly
|
||||
throw new TransactionValidationException("Expected an exact multiple of required $recipeItem (given: $haveCount, needed: $needCount)");
|
||||
}
|
||||
|
||||
$multiplier = intdiv($haveCount, $needCount);
|
||||
if($multiplier < 1){
|
||||
throw new TransactionValidationException("Expected more than zero items matching $recipeItem (given: $haveCount, needed: $needCount)");
|
||||
}
|
||||
if($iterations === 0){
|
||||
$iterations = $multiplier;
|
||||
}elseif($multiplier !== $iterations){
|
||||
//wrong count for this output, should match previous outputs
|
||||
throw new TransactionValidationException("Expected $recipeItem x$iterations, but found x$multiplier");
|
||||
}
|
||||
}
|
||||
|
||||
if($iterations < 1){
|
||||
throw new TransactionValidationException("Tried to craft zero times");
|
||||
}
|
||||
if(!empty($txItems)){
|
||||
//all items should be destroyed in this process
|
||||
throw new TransactionValidationException("Expected 0 ingredients left over, have " . count($txItems));
|
||||
}
|
||||
|
||||
return $iterations;
|
||||
}
|
||||
|
||||
public function validate() : void{
|
||||
$this->squashDuplicateSlotChanges();
|
||||
if(count($this->actions) < 1){
|
||||
throw new TransactionValidationException("Transaction must have at least one action to be executable");
|
||||
}
|
||||
|
||||
$this->matchItems($this->outputs, $this->inputs);
|
||||
|
||||
$failed = 0;
|
||||
foreach($this->source->getServer()->getCraftingManager()->matchRecipeByOutputs($this->outputs) as $recipe){
|
||||
try{
|
||||
//compute number of times recipe was crafted
|
||||
$this->repetitions = $this->matchRecipeItems($this->outputs, $recipe->getResultsFor($this->source->getCraftingGrid()), false);
|
||||
//assert that $repetitions x recipe ingredients should be consumed
|
||||
$this->matchRecipeItems($this->inputs, $recipe->getIngredientList(), true, $this->repetitions);
|
||||
|
||||
//Success!
|
||||
$this->recipe = $recipe;
|
||||
break;
|
||||
}catch(TransactionValidationException $e){
|
||||
//failed
|
||||
++$failed;
|
||||
}
|
||||
}
|
||||
|
||||
if($this->recipe === null){
|
||||
throw new TransactionValidationException("Unable to match a recipe to transaction (tried to match against $failed recipes)");
|
||||
}
|
||||
}
|
||||
|
||||
protected function callExecuteEvent() : bool{
|
||||
$ev = new CraftItemEvent($this, $this->recipe, $this->repetitions, $this->inputs, $this->outputs);
|
||||
$ev->call();
|
||||
return !$ev->isCancelled();
|
||||
}
|
||||
|
||||
protected function sendInventories() : void{
|
||||
parent::sendInventories();
|
||||
|
||||
/*
|
||||
* TODO: HACK!
|
||||
* we can't resend the contents of the crafting window, so we force the client to close it instead.
|
||||
* So people don't whine about messy desync issues when someone cancels CraftItemEvent, or when a crafting
|
||||
* transaction goes wrong.
|
||||
*/
|
||||
$this->source->sendDataPacket(ContainerClosePacket::create(ContainerIds::NONE));
|
||||
}
|
||||
}
|
338
src/inventory/transaction/InventoryTransaction.php
Normal file
338
src/inventory/transaction/InventoryTransaction.php
Normal file
@ -0,0 +1,338 @@
|
||||
<?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;
|
||||
|
||||
use pocketmine\event\inventory\InventoryTransactionEvent;
|
||||
use pocketmine\inventory\Inventory;
|
||||
use pocketmine\inventory\transaction\action\InventoryAction;
|
||||
use pocketmine\inventory\transaction\action\SlotChangeAction;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\player\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 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;
|
||||
/** @var Player */
|
||||
protected $source;
|
||||
|
||||
/** @var Inventory[] */
|
||||
protected $inventories = [];
|
||||
|
||||
/** @var InventoryAction[] */
|
||||
protected $actions = [];
|
||||
|
||||
/**
|
||||
* @param Player $source
|
||||
* @param InventoryAction[] $actions
|
||||
*/
|
||||
public function __construct(Player $source, array $actions = []){
|
||||
$this->source = $source;
|
||||
foreach($actions as $action){
|
||||
$this->addAction($action);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Player
|
||||
*/
|
||||
public function getSource() : Player{
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Inventory[]
|
||||
*/
|
||||
public function getInventories() : array{
|
||||
return $this->inventories;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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{
|
||||
return $this->actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InventoryAction $action
|
||||
*/
|
||||
public function addAction(InventoryAction $action) : void{
|
||||
if(!isset($this->actions[$hash = spl_object_id($action)])){
|
||||
$this->actions[$hash] = $action;
|
||||
$action->onAddToTransaction($this);
|
||||
}else{
|
||||
throw new \InvalidArgumentException("Tried to add the same action to a transaction twice");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param Inventory $inventory
|
||||
*/
|
||||
public function addInventory(Inventory $inventory) : void{
|
||||
if(!isset($this->inventories[$hash = spl_object_id($inventory)])){
|
||||
$this->inventories[$hash] = $inventory;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Item[] $needItems
|
||||
* @param Item[] $haveItems
|
||||
*
|
||||
* @throws TransactionValidationException
|
||||
*/
|
||||
protected function matchItems(array &$needItems, array &$haveItems) : void{
|
||||
foreach($this->actions as $key => $action){
|
||||
if(!$action->getTargetItem()->isNull()){
|
||||
$needItems[] = $action->getTargetItem();
|
||||
}
|
||||
|
||||
if(!$action->isValid($this->source)){
|
||||
throw new TransactionValidationException("Action " . get_class($action) . " is not valid in the current transaction");
|
||||
}
|
||||
|
||||
if(!$action->getSourceItem()->isNull()){
|
||||
$haveItems[] = $action->getSourceItem();
|
||||
}
|
||||
}
|
||||
|
||||
foreach($needItems as $i => $needItem){
|
||||
foreach($haveItems as $j => $haveItem){
|
||||
if($needItem->equals($haveItem)){
|
||||
$amount = min($needItem->getCount(), $haveItem->getCount());
|
||||
$needItem->setCount($needItem->getCount() - $amount);
|
||||
$haveItem->setCount($haveItem->getCount() - $amount);
|
||||
if($haveItem->getCount() === 0){
|
||||
unset($haveItems[$j]);
|
||||
}
|
||||
if($needItem->getCount() === 0){
|
||||
unset($needItems[$i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over SlotChangeActions in this transaction and compacts any which refer to the same slot in the same
|
||||
* inventory so they can be correctly handled.
|
||||
*
|
||||
* Under normal circumstances, the same slot would never be changed more than once in a single transaction. However,
|
||||
* due to the way things like the crafting grid are "implemented" in MCPE 1.2 (a.k.a. hacked-in), we may get
|
||||
* multiple slot changes referring to the same slot in a single transaction. These multiples are not even guaranteed
|
||||
* to be in the correct order (slot splitting in the crafting grid for example, causes the actions to be sent in the
|
||||
* wrong order), so this method also tries to chain them into order.
|
||||
*/
|
||||
protected function squashDuplicateSlotChanges() : void{
|
||||
/** @var SlotChangeAction[][] $slotChanges */
|
||||
$slotChanges = [];
|
||||
/** @var Inventory[] $inventories */
|
||||
$inventories = [];
|
||||
/** @var int[] $slots */
|
||||
$slots = [];
|
||||
|
||||
foreach($this->actions as $key => $action){
|
||||
if($action instanceof SlotChangeAction){
|
||||
$slotChanges[$h = (spl_object_hash($action->getInventory()) . "@" . $action->getSlot())][] = $action;
|
||||
$inventories[$h] = $action->getInventory();
|
||||
$slots[$h] = $action->getSlot();
|
||||
}
|
||||
}
|
||||
|
||||
foreach($slotChanges as $hash => $list){
|
||||
if(count($list) === 1){ //No need to compact slot changes if there is only one on this slot
|
||||
continue;
|
||||
}
|
||||
|
||||
$inventory = $inventories[$hash];
|
||||
$slot = $slots[$hash];
|
||||
if(!$inventory->slotExists($slot)){ //this can get hit for crafting tables because the validation happens after this compaction
|
||||
throw new TransactionValidationException("Slot $slot does not exist in inventory " . get_class($inventory));
|
||||
}
|
||||
$sourceItem = $inventory->getItem($slot);
|
||||
|
||||
$targetItem = $this->findResultItem($sourceItem, $list);
|
||||
if($targetItem === null){
|
||||
throw new TransactionValidationException("Failed to compact " . count($list) . " duplicate actions");
|
||||
}
|
||||
|
||||
foreach($list as $action){
|
||||
unset($this->actions[spl_object_id($action)]);
|
||||
}
|
||||
|
||||
if(!$targetItem->equalsExact($sourceItem)){
|
||||
//sometimes we get actions on the crafting grid whose source and target items are the same, so dump them
|
||||
$this->addAction(new SlotChangeAction($inventory, $slot, $sourceItem, $targetItem));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Item $needOrigin
|
||||
* @param SlotChangeAction[] $possibleActions
|
||||
*
|
||||
* @return null|Item
|
||||
*/
|
||||
protected function findResultItem(Item $needOrigin, array $possibleActions) : ?Item{
|
||||
assert(!empty($possibleActions));
|
||||
|
||||
foreach($possibleActions as $i => $action){
|
||||
if($action->getSourceItem()->equalsExact($needOrigin)){
|
||||
$newList = $possibleActions;
|
||||
unset($newList[$i]);
|
||||
if(empty($newList)){
|
||||
return $action->getTargetItem();
|
||||
}
|
||||
$result = $this->findResultItem($action->getTargetItem(), $newList);
|
||||
if($result !== null){
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the transaction can execute.
|
||||
*
|
||||
* @throws TransactionValidationException
|
||||
*/
|
||||
public function validate() : void{
|
||||
$this->squashDuplicateSlotChanges();
|
||||
|
||||
$haveItems = [];
|
||||
$needItems = [];
|
||||
$this->matchItems($needItems, $haveItems);
|
||||
if(count($this->actions) === 0){
|
||||
throw new TransactionValidationException("Inventory transaction must have at least one action to be executable");
|
||||
}
|
||||
|
||||
if(count($haveItems) > 0){
|
||||
throw new TransactionValidationException("Transaction does not balance (tried to destroy some items)");
|
||||
}
|
||||
if(count($needItems) > 0){
|
||||
throw new TransactionValidationException("Transaction does not balance (tried to create some items)");
|
||||
}
|
||||
}
|
||||
|
||||
protected function sendInventories() : void{
|
||||
foreach($this->inventories as $inventory){
|
||||
$this->source->getNetworkSession()->getInvManager()->syncContents($inventory);
|
||||
}
|
||||
}
|
||||
|
||||
protected function callExecuteEvent() : bool{
|
||||
$ev = new InventoryTransactionEvent($this);
|
||||
$ev->call();
|
||||
return !$ev->isCancelled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the group of actions, returning whether the transaction executed successfully or not.
|
||||
* @return bool
|
||||
*
|
||||
* @throws TransactionValidationException
|
||||
*/
|
||||
public function execute() : bool{
|
||||
if($this->hasExecuted()){
|
||||
$this->sendInventories();
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->shuffleActions();
|
||||
|
||||
$this->validate();
|
||||
|
||||
if(!$this->callExecuteEvent()){
|
||||
$this->sendInventories();
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach($this->actions as $action){
|
||||
if(!$action->onPreExecute($this->source)){
|
||||
$this->sendInventories();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
foreach($this->actions as $action){
|
||||
$action->execute($this->source);
|
||||
}
|
||||
|
||||
$this->hasExecuted = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasExecuted() : bool{
|
||||
return $this->hasExecuted;
|
||||
}
|
||||
}
|
28
src/inventory/transaction/TransactionValidationException.php
Normal file
28
src/inventory/transaction/TransactionValidationException.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?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;
|
||||
|
||||
class TransactionValidationException extends \RuntimeException{
|
||||
|
||||
}
|
48
src/inventory/transaction/action/CreateItemAction.php
Normal file
48
src/inventory/transaction/action/CreateItemAction.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?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;
|
||||
|
||||
use pocketmine\inventory\CreativeInventory;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
/**
|
||||
* This action is used by creative players to balance transactions involving the creative inventory menu.
|
||||
* The source item is the item being created ("taken" from the creative menu).
|
||||
*/
|
||||
class CreateItemAction extends InventoryAction{
|
||||
|
||||
public function __construct(Item $sourceItem){
|
||||
parent::__construct($sourceItem, ItemFactory::air());
|
||||
}
|
||||
|
||||
public function isValid(Player $source) : bool{
|
||||
return !$source->hasFiniteResources() and CreativeInventory::contains($this->sourceItem);
|
||||
}
|
||||
|
||||
public function execute(Player $source) : void{
|
||||
//NOOP
|
||||
}
|
||||
}
|
47
src/inventory/transaction/action/DestroyItemAction.php
Normal file
47
src/inventory/transaction/action/DestroyItemAction.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?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;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
/**
|
||||
* This action type shows up when a creative player puts an item into the creative inventory menu to destroy it.
|
||||
* The output is the item destroyed. You can think of this action type like setting an item into /dev/null
|
||||
*/
|
||||
class DestroyItemAction extends InventoryAction{
|
||||
|
||||
public function __construct(Item $targetItem){
|
||||
parent::__construct(ItemFactory::air(), $targetItem);
|
||||
}
|
||||
|
||||
public function isValid(Player $source) : bool{
|
||||
return !$source->hasFiniteResources();
|
||||
}
|
||||
|
||||
public function execute(Player $source) : void{
|
||||
//NOOP
|
||||
}
|
||||
}
|
62
src/inventory/transaction/action/DropItemAction.php
Normal file
62
src/inventory/transaction/action/DropItemAction.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?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;
|
||||
|
||||
use pocketmine\event\player\PlayerDropItemEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
/**
|
||||
* Represents an action involving dropping an item into the world.
|
||||
*/
|
||||
class DropItemAction extends InventoryAction{
|
||||
|
||||
public function __construct(Item $targetItem){
|
||||
parent::__construct(ItemFactory::air(), $targetItem);
|
||||
}
|
||||
|
||||
public function isValid(Player $source) : bool{
|
||||
return !$this->targetItem->isNull();
|
||||
}
|
||||
|
||||
public function onPreExecute(Player $source) : bool{
|
||||
$ev = new PlayerDropItemEvent($source, $this->targetItem);
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops the target item in front of the player.
|
||||
*
|
||||
* @param Player $source
|
||||
*/
|
||||
public function execute(Player $source) : void{
|
||||
$source->dropItem($this->targetItem);
|
||||
}
|
||||
}
|
97
src/inventory/transaction/action/InventoryAction.php
Normal file
97
src/inventory/transaction/action/InventoryAction.php
Normal file
@ -0,0 +1,97 @@
|
||||
<?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;
|
||||
|
||||
use pocketmine\inventory\transaction\InventoryTransaction;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
/**
|
||||
* Represents an action involving a change that applies in some way to an inventory or other item-source.
|
||||
*/
|
||||
abstract class InventoryAction{
|
||||
/** @var Item */
|
||||
protected $sourceItem;
|
||||
/** @var Item */
|
||||
protected $targetItem;
|
||||
|
||||
public function __construct(Item $sourceItem, Item $targetItem){
|
||||
$this->sourceItem = $sourceItem;
|
||||
$this->targetItem = $targetItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the item that was present before the action took place.
|
||||
* @return Item
|
||||
*/
|
||||
public function getSourceItem() : Item{
|
||||
return clone $this->sourceItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the item that the action attempted to replace the source item with.
|
||||
* @return Item
|
||||
*/
|
||||
public function getTargetItem() : Item{
|
||||
return clone $this->targetItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this action is currently valid. This should perform any necessary sanity checks.
|
||||
*
|
||||
* @param Player $source
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function isValid(Player $source) : bool;
|
||||
|
||||
/**
|
||||
* Called when the action is added to the specified InventoryTransaction.
|
||||
*
|
||||
* @param InventoryTransaction $transaction
|
||||
*/
|
||||
public function onAddToTransaction(InventoryTransaction $transaction) : void{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by inventory transactions before any actions are processed. If this returns false, the transaction will
|
||||
* be cancelled.
|
||||
*
|
||||
* @param Player $source
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function onPreExecute(Player $source) : bool{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs actions needed to complete the inventory-action server-side. This will only be called if the transaction
|
||||
* which it is part of is considered valid.
|
||||
*
|
||||
* @param Player $source
|
||||
*/
|
||||
abstract public function execute(Player $source) : void;
|
||||
}
|
107
src/inventory/transaction/action/SlotChangeAction.php
Normal file
107
src/inventory/transaction/action/SlotChangeAction.php
Normal file
@ -0,0 +1,107 @@
|
||||
<?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;
|
||||
|
||||
use pocketmine\inventory\Inventory;
|
||||
use pocketmine\inventory\transaction\InventoryTransaction;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
/**
|
||||
* Represents an action causing a change in an inventory slot.
|
||||
*/
|
||||
class SlotChangeAction extends InventoryAction{
|
||||
|
||||
/** @var Inventory */
|
||||
protected $inventory;
|
||||
/** @var int */
|
||||
private $inventorySlot;
|
||||
|
||||
/**
|
||||
* @param Inventory $inventory
|
||||
* @param int $inventorySlot
|
||||
* @param Item $sourceItem
|
||||
* @param Item $targetItem
|
||||
*/
|
||||
public function __construct(Inventory $inventory, int $inventorySlot, Item $sourceItem, Item $targetItem){
|
||||
parent::__construct($sourceItem, $targetItem);
|
||||
$this->inventory = $inventory;
|
||||
$this->inventorySlot = $inventorySlot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the inventory involved in this action.
|
||||
*
|
||||
* @return Inventory
|
||||
*/
|
||||
public function getInventory() : Inventory{
|
||||
return $this->inventory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the slot in the inventory which this action modified.
|
||||
* @return int
|
||||
*/
|
||||
public function getSlot() : int{
|
||||
return $this->inventorySlot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the item in the inventory at the specified slot is the same as this action's source item.
|
||||
*
|
||||
* @param Player $source
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid(Player $source) : bool{
|
||||
return (
|
||||
$this->inventory->slotExists($this->inventorySlot) and
|
||||
$this->inventory->getItem($this->inventorySlot)->equalsExact($this->sourceItem)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds this action's target inventory to the transaction's inventory list.
|
||||
*
|
||||
* @param InventoryTransaction $transaction
|
||||
*
|
||||
*/
|
||||
public function onAddToTransaction(InventoryTransaction $transaction) : void{
|
||||
$transaction->addInventory($this->inventory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the item into the target inventory.
|
||||
*
|
||||
* @param Player $source
|
||||
*/
|
||||
public function execute(Player $source) : void{
|
||||
$this->inventory->setItem($this->inventorySlot, $this->targetItem, false);
|
||||
foreach($this->inventory->getViewers() as $viewer){
|
||||
if($viewer !== $source){
|
||||
$viewer->getNetworkSession()->getInvManager()->syncSlot($this->inventory, $this->inventorySlot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user