mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-11 05:55:33 +00:00
Many many changes related to inventory transactions, fixed item dropping, fixed creative menu
This commit is contained in:
parent
c1ff7bbef4
commit
8958b3c51c
@ -73,7 +73,6 @@ use pocketmine\event\server\DataPacketSendEvent;
|
||||
use pocketmine\event\TextContainer;
|
||||
use pocketmine\event\Timings;
|
||||
use pocketmine\event\TranslationContainer;
|
||||
use pocketmine\inventory\BaseTransaction;
|
||||
use pocketmine\inventory\BigShapedRecipe;
|
||||
use pocketmine\inventory\BigShapelessRecipe;
|
||||
use pocketmine\inventory\FurnaceInventory;
|
||||
@ -82,7 +81,7 @@ use pocketmine\inventory\PlayerCursorInventory;
|
||||
use pocketmine\inventory\PlayerInventory;
|
||||
use pocketmine\inventory\ShapedRecipe;
|
||||
use pocketmine\inventory\ShapelessRecipe;
|
||||
use pocketmine\inventory\SimpleTransactionGroup;
|
||||
use pocketmine\inventory\transaction\SimpleInventoryTransaction;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\ChunkLoader;
|
||||
use pocketmine\level\format\Chunk;
|
||||
@ -120,7 +119,6 @@ use pocketmine\network\mcpe\protocol\ContainerClosePacket;
|
||||
use pocketmine\network\mcpe\protocol\CraftingEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\DataPacket;
|
||||
use pocketmine\network\mcpe\protocol\DisconnectPacket;
|
||||
use pocketmine\network\mcpe\protocol\DropItemPacket;
|
||||
use pocketmine\network\mcpe\protocol\EntityEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\InteractPacket;
|
||||
use pocketmine\network\mcpe\protocol\InventoryTransactionPacket;
|
||||
@ -235,8 +233,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
public $speed = null;
|
||||
|
||||
public $achievements = [];
|
||||
/** @var SimpleTransactionGroup */
|
||||
protected $currentTransaction = null;
|
||||
|
||||
public $craftingType = 0; //0 = 2x2 crafting, 1 = 3x3 crafting, 2 = stonecutter
|
||||
|
||||
/** @var PlayerCursorInventory */
|
||||
@ -2161,28 +2158,38 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
public function handleInventoryTransaction(InventoryTransactionPacket $packet) : bool{
|
||||
switch($packet->transactionData->transactionType){
|
||||
case InventoryTransactionPacket::TYPE_NORMAL:
|
||||
$transaction = new SimpleTransactionGroup($this);
|
||||
$transaction = new SimpleInventoryTransaction($this);
|
||||
|
||||
foreach($packet->actions as $action){
|
||||
if($action->inventorySource->sourceType !== InventoryTransactionPacket::SOURCE_CONTAINER){
|
||||
return false; //TODO: handle other source types
|
||||
}
|
||||
|
||||
if($action->inventorySource->containerId === ContainerIds::ARMOR){
|
||||
$transaction->addTransaction(new BaseTransaction($this->inventory, $action->inventorySlot + $this->inventory->getSize(), $action->oldItem, $action->newItem));
|
||||
continue;
|
||||
}
|
||||
|
||||
$transaction->addTransaction(new BaseTransaction($this->windowIndex[$action->inventorySource->containerId], $action->inventorySlot, $action->oldItem, $action->newItem));
|
||||
$transaction->addAction($action);
|
||||
}
|
||||
|
||||
if(!$transaction->execute()){
|
||||
//need to resend/revert/whatever (TODO)
|
||||
foreach($transaction->getInventories() as $inventory){
|
||||
$inventory->sendContents($this);
|
||||
if($inventory instanceof PlayerInventory){
|
||||
$inventory->sendArmorContents($this);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: check more stuff that might need reversion
|
||||
return false; //oops!
|
||||
}
|
||||
|
||||
//TODO: fix achievement for getting iron from furnace
|
||||
|
||||
return true;
|
||||
case InventoryTransactionPacket::TYPE_MISMATCH:
|
||||
if(count($packet->actions) > 0){
|
||||
$this->server->getLogger()->debug("Expected 0 actions for mismatch, got " . count($packet->actions) . ", " . json_encode($packet->actions));
|
||||
}
|
||||
foreach($this->windowIndex as $id => $inventory){
|
||||
$inventory->sendContents($this);
|
||||
if($inventory instanceof PlayerInventory){
|
||||
$inventory->sendArmorContents($this);
|
||||
}
|
||||
}
|
||||
$this->inventory->sendHotbar();
|
||||
return true;
|
||||
case InventoryTransactionPacket::TYPE_USE_ITEM:
|
||||
$blockVector = new Vector3($packet->transactionData->x, $packet->transactionData->y, $packet->transactionData->z);
|
||||
@ -2463,6 +2470,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
case InventoryTransactionPacket::TYPE_RELEASE_ITEM:
|
||||
//TODO
|
||||
break;
|
||||
default:
|
||||
$this->inventory->sendContents($this);
|
||||
break;
|
||||
|
||||
}
|
||||
return false; //TODO
|
||||
@ -2805,25 +2815,27 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleDropItem(DropItemPacket $packet) : bool{
|
||||
if($this->spawned === false or !$this->isAlive()){
|
||||
/**
|
||||
* Drops an item on the ground in front of the player. Returns if the item drop was successful.
|
||||
*
|
||||
* @param Item $item
|
||||
* @return bool if the item was dropped or if the item was null
|
||||
*/
|
||||
public function dropItem(Item $item){
|
||||
if(!$this->spawned or !$this->isAlive()){
|
||||
return false;
|
||||
}
|
||||
|
||||
if($item->isNull()){
|
||||
$this->server->getLogger()->debug($this->getName() . " attempted to drop a null item (" . $item . ")");
|
||||
return true;
|
||||
}
|
||||
|
||||
if($packet->item->getId() === Item::AIR){
|
||||
// Windows 10 Edition drops the contents of the crafting grid on container close - including air.
|
||||
return true;
|
||||
}
|
||||
|
||||
$item = $this->inventory->getItemInHand();
|
||||
$ev = new PlayerDropItemEvent($this, $item);
|
||||
$this->server->getPluginManager()->callEvent($ev);
|
||||
$this->server->getPluginManager()->callEvent($ev = new PlayerDropItemEvent($this, $item));
|
||||
if($ev->isCancelled()){
|
||||
$this->inventory->sendContents($this);
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->inventory->setItemInHand(Item::get(Item::AIR, 0, 1));
|
||||
$motion = $this->getDirectionVector()->multiply(0.4);
|
||||
|
||||
$this->level->dropItem($this->add(0, 1.3, 0), $item, $motion, 40);
|
||||
@ -2839,7 +2851,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
$this->craftingType = 0;
|
||||
$this->currentTransaction = null;
|
||||
|
||||
if(isset($this->windowIndex[$packet->windowId])){
|
||||
$this->server->getPluginManager()->callEvent(new InventoryCloseEvent($this->windowIndex[$packet->windowId], $this));
|
||||
$this->removeWindow($this->windowIndex[$packet->windowId]);
|
||||
@ -3477,12 +3489,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->perm->clearPermissions();
|
||||
$this->perm = null;
|
||||
}
|
||||
|
||||
if($this->inventory !== null){
|
||||
$this->inventory = null;
|
||||
$this->currentTransaction = null;
|
||||
}
|
||||
|
||||
}catch(\Throwable $e){
|
||||
$this->server->getLogger()->logException($e);
|
||||
}finally{
|
||||
@ -3809,6 +3815,15 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return ContainerIds::NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $windowId
|
||||
*
|
||||
* @return Inventory|null
|
||||
*/
|
||||
public function getWindow(int $windowId){
|
||||
return $this->windowIndex[$windowId] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the created/existing window id
|
||||
*
|
||||
|
@ -25,7 +25,7 @@ namespace pocketmine\event\inventory;
|
||||
|
||||
use pocketmine\event\Cancellable;
|
||||
use pocketmine\event\Event;
|
||||
use pocketmine\inventory\TransactionGroup;
|
||||
use pocketmine\inventory\transaction\InventoryTransaction;
|
||||
|
||||
/**
|
||||
* Called when there is a transaction between two Inventory objects.
|
||||
@ -34,21 +34,21 @@ use pocketmine\inventory\TransactionGroup;
|
||||
class InventoryTransactionEvent extends Event implements Cancellable{
|
||||
public static $handlerList = null;
|
||||
|
||||
/** @var TransactionGroup */
|
||||
private $ts;
|
||||
/** @var InventoryTransaction */
|
||||
private $transaction;
|
||||
|
||||
/**
|
||||
* @param TransactionGroup $ts
|
||||
* @param InventoryTransaction $transaction
|
||||
*/
|
||||
public function __construct(TransactionGroup $ts){
|
||||
$this->ts = $ts;
|
||||
public function __construct(InventoryTransaction $transaction){
|
||||
$this->transaction = $transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TransactionGroup
|
||||
* @return InventoryTransaction
|
||||
*/
|
||||
public function getTransaction() : TransactionGroup{
|
||||
return $this->ts;
|
||||
public function getTransaction() : InventoryTransaction{
|
||||
return $this->transaction;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -140,7 +140,6 @@ abstract class BaseInventory implements Inventory{
|
||||
if($holder instanceof Entity){
|
||||
Server::getInstance()->getPluginManager()->callEvent($ev = new EntityInventoryChangeEvent($holder, $this->getItem($index), $item, $index));
|
||||
if($ev->isCancelled()){
|
||||
$this->sendSlot($index, $this->getViewers());
|
||||
return false;
|
||||
}
|
||||
$item = $ev->getNewItem();
|
||||
|
@ -1,73 +0,0 @@
|
||||
<?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\item\Item;
|
||||
|
||||
class BaseTransaction implements Transaction{
|
||||
/** @var Inventory */
|
||||
protected $inventory;
|
||||
/** @var int */
|
||||
protected $slot;
|
||||
/** @var Item */
|
||||
protected $sourceItem;
|
||||
/** @var Item */
|
||||
protected $targetItem;
|
||||
/** @var float */
|
||||
protected $creationTime;
|
||||
|
||||
/**
|
||||
* @param Inventory $inventory
|
||||
* @param int $slot
|
||||
* @param Item $sourceItem
|
||||
* @param Item $targetItem
|
||||
*/
|
||||
public function __construct(Inventory $inventory, int $slot, Item $sourceItem, Item $targetItem){
|
||||
$this->inventory = $inventory;
|
||||
$this->slot = $slot;
|
||||
$this->sourceItem = clone $sourceItem;
|
||||
$this->targetItem = clone $targetItem;
|
||||
$this->creationTime = microtime(true);
|
||||
}
|
||||
|
||||
public function getCreationTime() : float{
|
||||
return $this->creationTime;
|
||||
}
|
||||
|
||||
public function getInventory() : Inventory{
|
||||
return $this->inventory;
|
||||
}
|
||||
|
||||
public function getSlot() : int{
|
||||
return $this->slot;
|
||||
}
|
||||
|
||||
public function getSourceItem() : Item{
|
||||
return clone $this->sourceItem;
|
||||
}
|
||||
|
||||
public function getTargetItem() : Item{
|
||||
return clone $this->targetItem;
|
||||
}
|
||||
}
|
@ -351,6 +351,12 @@ class PlayerInventory extends BaseInventory{
|
||||
}
|
||||
|
||||
public function setItem(int $index, Item $item, bool $send = true) : bool{
|
||||
if($item->isNull()){
|
||||
$item = Item::get(Item::AIR, 0, 0);
|
||||
}else{
|
||||
$item = clone $item;
|
||||
}
|
||||
|
||||
if($index >= $this->getSize()){ //Armor change
|
||||
Server::getInstance()->getPluginManager()->callEvent($ev = new EntityArmorChangeEvent($this->getHolder(), $this->getItem($index), $item, $index));
|
||||
if($ev->isCancelled() and $this->getHolder() instanceof Human){
|
||||
@ -367,9 +373,8 @@ class PlayerInventory extends BaseInventory{
|
||||
$item = $ev->getNewItem();
|
||||
}
|
||||
|
||||
|
||||
$old = $this->getItem($index);
|
||||
$this->slots[$index] = clone $item;
|
||||
$this->slots[$index] = $item;
|
||||
$this->onSlotChange($index, $old, $send);
|
||||
|
||||
return true;
|
||||
|
@ -21,9 +21,12 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\inventory;
|
||||
namespace pocketmine\inventory\transaction;
|
||||
|
||||
interface TransactionGroup{
|
||||
use pocketmine\inventory\Inventory;
|
||||
use pocketmine\inventory\transaction\action\InventoryAction;
|
||||
|
||||
interface InventoryTransaction{
|
||||
|
||||
/**
|
||||
* @return float
|
||||
@ -31,9 +34,9 @@ interface TransactionGroup{
|
||||
public function getCreationTime() : float;
|
||||
|
||||
/**
|
||||
* @return Transaction[]
|
||||
* @return InventoryAction[]
|
||||
*/
|
||||
public function getTransactions() : array;
|
||||
public function getActions() : array;
|
||||
|
||||
/**
|
||||
* @return Inventory[]
|
||||
@ -41,9 +44,9 @@ interface TransactionGroup{
|
||||
public function getInventories() : array;
|
||||
|
||||
/**
|
||||
* @param Transaction $transaction
|
||||
* @param InventoryAction $action
|
||||
*/
|
||||
public function addTransaction(Transaction $transaction);
|
||||
public function addAction(InventoryAction $action);
|
||||
|
||||
/**
|
||||
* @return bool
|
@ -21,17 +21,21 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\inventory;
|
||||
namespace pocketmine\inventory\transaction;
|
||||
|
||||
use pocketmine\event\inventory\InventoryTransactionEvent;
|
||||
use pocketmine\inventory\Inventory;
|
||||
use pocketmine\inventory\transaction\InventoryTransaction;
|
||||
use pocketmine\inventory\transaction\action\InventoryAction;
|
||||
use pocketmine\inventory\transaction\action\SlotChangeAction;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\Server;
|
||||
|
||||
/**
|
||||
* This TransactionGroup only allows doing Transaction between one / two inventories
|
||||
* This InventoryTransaction only allows doing Transaction between one / two inventories
|
||||
*/
|
||||
class SimpleTransactionGroup implements TransactionGroup{
|
||||
class SimpleInventoryTransaction implements InventoryTransaction{
|
||||
/** @var float */
|
||||
private $creationTime;
|
||||
protected $hasExecuted = false;
|
||||
@ -41,8 +45,8 @@ class SimpleTransactionGroup implements TransactionGroup{
|
||||
/** @var Inventory[] */
|
||||
protected $inventories = [];
|
||||
|
||||
/** @var Transaction[] */
|
||||
protected $transactions = [];
|
||||
/** @var InventoryAction[] */
|
||||
protected $actions = [];
|
||||
|
||||
/**
|
||||
* @param Player $source
|
||||
@ -71,27 +75,22 @@ class SimpleTransactionGroup implements TransactionGroup{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Transaction[]
|
||||
* @return InventoryAction[]
|
||||
*/
|
||||
public function getTransactions() : array{
|
||||
return $this->transactions;
|
||||
public function getActions() : array{
|
||||
return $this->actions;
|
||||
}
|
||||
|
||||
public function addTransaction(Transaction $transaction){
|
||||
if(isset($this->transactions[spl_object_hash($transaction)])){
|
||||
public function addAction(InventoryAction $action){
|
||||
if(isset($this->actions[spl_object_hash($action)])){
|
||||
return;
|
||||
}
|
||||
foreach($this->transactions as $hash => $tx){
|
||||
if($tx->getInventory() === $transaction->getInventory() and $tx->getSlot() === $transaction->getSlot()){
|
||||
if($transaction->getCreationTime() >= $tx->getCreationTime()){
|
||||
unset($this->transactions[$hash]);
|
||||
}else{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$this->actions[spl_object_hash($action)] = $action;
|
||||
if($action instanceof SlotChangeAction){
|
||||
$action->setInventoryFrom($this->source);
|
||||
$this->inventories[spl_object_hash($action->getInventory())] = $action->getInventory();
|
||||
}
|
||||
$this->transactions[spl_object_hash($transaction)] = $transaction;
|
||||
$this->inventories[spl_object_hash($transaction->getInventory())] = $transaction->getInventory();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,17 +100,17 @@ class SimpleTransactionGroup implements TransactionGroup{
|
||||
* @return bool
|
||||
*/
|
||||
protected function matchItems(array &$needItems, array &$haveItems) : bool{
|
||||
foreach($this->transactions as $key => $ts){
|
||||
if($ts->getTargetItem()->getId() !== Item::AIR){
|
||||
$needItems[] = $ts->getTargetItem();
|
||||
foreach($this->actions as $key => $action){
|
||||
if($action->getTargetItem()->getId() !== Item::AIR){
|
||||
$needItems[] = $action->getTargetItem();
|
||||
}
|
||||
$checkSourceItem = $ts->getInventory()->getItem($ts->getSlot());
|
||||
$sourceItem = $ts->getSourceItem();
|
||||
if(!$checkSourceItem->equals($sourceItem) or $sourceItem->getCount() !== $checkSourceItem->getCount()){
|
||||
|
||||
if(!$action->isValid($this->source)){
|
||||
return false;
|
||||
}
|
||||
if($sourceItem->getId() !== Item::AIR){
|
||||
$haveItems[] = $sourceItem;
|
||||
|
||||
if($action->getSourceItem()->getId() !== Item::AIR){
|
||||
$haveItems[] = $action->getSourceItem();
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,7 +138,7 @@ class SimpleTransactionGroup implements TransactionGroup{
|
||||
$haveItems = [];
|
||||
$needItems = [];
|
||||
|
||||
return $this->matchItems($needItems, $haveItems) and count($this->transactions) > 0 and ((count($haveItems) === 0 and count($needItems) === 0) or $this->source->isCreative(true));
|
||||
return $this->matchItems($needItems, $haveItems) and count($this->actions) > 0 and count($haveItems) === 0 and count($needItems) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -152,18 +151,15 @@ class SimpleTransactionGroup implements TransactionGroup{
|
||||
|
||||
Server::getInstance()->getPluginManager()->callEvent($ev = new InventoryTransactionEvent($this));
|
||||
if($ev->isCancelled()){
|
||||
foreach($this->inventories as $inventory){
|
||||
if($inventory instanceof PlayerInventory){
|
||||
$inventory->sendArmorContents($this->getSource());
|
||||
}
|
||||
$inventory->sendContents($this->getSource());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach($this->transactions as $transaction){
|
||||
$transaction->getInventory()->setItem($transaction->getSlot(), $transaction->getTargetItem());
|
||||
foreach($this->actions as $action){
|
||||
if($action->execute($this->source)){
|
||||
$action->onExecuteSuccess($this->source);
|
||||
}else{
|
||||
$action->onExecuteFail($this->source);
|
||||
}
|
||||
}
|
||||
|
||||
$this->hasExecuted = true;
|
@ -0,0 +1,85 @@
|
||||
<?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\Player;
|
||||
|
||||
class CreativeInventoryAction extends InventoryAction{
|
||||
|
||||
/**
|
||||
* Player put an item into the creative window to destroy it.
|
||||
*/
|
||||
const TYPE_DELETE_ITEM = 0;
|
||||
/**
|
||||
* Player took an item from the creative window.
|
||||
*/
|
||||
const TYPE_CREATE_ITEM = 1;
|
||||
|
||||
protected $actionType;
|
||||
|
||||
public function __construct(Item $sourceItem, Item $targetItem, int $actionType){
|
||||
parent::__construct($sourceItem, $targetItem);
|
||||
$this->actionType = $actionType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the player is in creative, and (if creating an item) that the item exists in the creative inventory.
|
||||
*
|
||||
* @param Player $source
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid(Player $source) : bool{
|
||||
return $source->isCreative(true) and
|
||||
($this->actionType === self::TYPE_DELETE_ITEM or Item::getCreativeItemIndex($this->sourceItem) !== -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the action.
|
||||
*/
|
||||
public function getActionType() : int{
|
||||
return $this->actionType;
|
||||
}
|
||||
|
||||
/**
|
||||
* No need to do anything extra here: this type just provides a place for items to disappear or appear from.
|
||||
*
|
||||
* @param Player $source
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function execute(Player $source) : bool{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function onExecuteSuccess(Player $source){
|
||||
|
||||
}
|
||||
|
||||
public function onExecuteFail(Player $source){
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -21,34 +21,41 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\inventory;
|
||||
namespace pocketmine\inventory\transaction\action;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\Player;
|
||||
|
||||
interface Transaction{
|
||||
/**
|
||||
* Represents an action involving dropping an item into the world.
|
||||
*/
|
||||
class DropItemAction extends InventoryAction{
|
||||
|
||||
/**
|
||||
* @return Inventory
|
||||
* Verifies that the source item of a drop-item action must be air. This is not strictly necessary, just a sanity
|
||||
* check.
|
||||
*
|
||||
* @param Player $source
|
||||
* @return bool
|
||||
*/
|
||||
public function getInventory() : Inventory;
|
||||
public function isValid(Player $source) : bool{
|
||||
return $this->sourceItem->isNull();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
* Drops the target item in front of the player.
|
||||
*
|
||||
* @param Player $source
|
||||
* @return bool
|
||||
*/
|
||||
public function getSlot() : int;
|
||||
public function execute(Player $source) : bool{
|
||||
return $source->dropItem($this->targetItem);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Item
|
||||
*/
|
||||
public function getSourceItem() : Item;
|
||||
public function onExecuteSuccess(Player $source){
|
||||
|
||||
/**
|
||||
* @return Item
|
||||
*/
|
||||
public function getTargetItem() : Item;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function getCreationTime() : float;
|
||||
public function onExecuteFail(Player $source){
|
||||
|
||||
}
|
||||
}
|
102
src/pocketmine/inventory/transaction/action/InventoryAction.php
Normal file
102
src/pocketmine/inventory/transaction/action/InventoryAction.php
Normal file
@ -0,0 +1,102 @@
|
||||
<?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\Player;
|
||||
|
||||
/**
|
||||
* Represents an action involving a change that applies in some way to an inventory or other item-source.
|
||||
*/
|
||||
abstract class InventoryAction{
|
||||
|
||||
/** @var float */
|
||||
private $creationTime;
|
||||
/** @var Item */
|
||||
protected $sourceItem;
|
||||
/** @var Item */
|
||||
protected $targetItem;
|
||||
|
||||
public function __construct(Item $sourceItem, Item $targetItem){
|
||||
$this->sourceItem = $sourceItem;
|
||||
$this->targetItem = $targetItem;
|
||||
|
||||
$this->creationTime = microtime(true);
|
||||
}
|
||||
|
||||
public function getCreationTime() : float{
|
||||
return $this->creationTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Performs actions needed to complete the inventory-action server-side. Returns if it was successful. Will return
|
||||
* false if plugins cancelled events. This will only be called if the transaction which it is part of is considered
|
||||
* valid.
|
||||
*
|
||||
* @param Player $source
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function execute(Player $source) : bool;
|
||||
|
||||
/**
|
||||
* Performs additional actions when this inventory-action completed successfully.
|
||||
*
|
||||
* @param Player $source
|
||||
*/
|
||||
abstract public function onExecuteSuccess(Player $source);
|
||||
|
||||
/**
|
||||
* Performs additional actions when this inventory-action did not complete successfully.
|
||||
*
|
||||
* @param Player $source
|
||||
*/
|
||||
abstract public function onExecuteFail(Player $source);
|
||||
|
||||
}
|
126
src/pocketmine/inventory/transaction/action/SlotChangeAction.php
Normal file
126
src/pocketmine/inventory/transaction/action/SlotChangeAction.php
Normal file
@ -0,0 +1,126 @@
|
||||
<?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\item\Item;
|
||||
use pocketmine\Player;
|
||||
|
||||
/**
|
||||
* Represents an action causing a change in an inventory slot.
|
||||
*/
|
||||
class SlotChangeAction extends InventoryAction{
|
||||
|
||||
/** @var Inventory|null */
|
||||
protected $inventory;
|
||||
/** @var int */
|
||||
private $inventorySlot;
|
||||
/** @var int */
|
||||
private $containerId;
|
||||
|
||||
/**
|
||||
* @param Item $sourceItem
|
||||
* @param Item $targetItem
|
||||
* @param int $containerId
|
||||
* @param int $inventorySlot
|
||||
*/
|
||||
public function __construct(Item $sourceItem, Item $targetItem, int $containerId, int $inventorySlot){
|
||||
parent::__construct($sourceItem, $targetItem);
|
||||
$this->inventorySlot = $inventorySlot;
|
||||
$this->containerId = $containerId;
|
||||
}
|
||||
|
||||
public function getContainerId() : int{
|
||||
return $this->containerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the inventory involved in this action. Will return null if the action has not yet been fully initialized.
|
||||
*
|
||||
* @return Inventory|null
|
||||
*/
|
||||
public function getInventory(){
|
||||
return $this->inventory;
|
||||
}
|
||||
|
||||
public function setInventoryFrom(Player $player){
|
||||
$inventory = $player->getWindow($this->containerId);
|
||||
if($inventory === null){
|
||||
throw new \InvalidStateException("Player " . $player->getName() . " has no open container with ID " . $this->containerId);
|
||||
}
|
||||
|
||||
$this->inventory = $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{
|
||||
$check = $this->inventory->getItem($this->inventorySlot);
|
||||
return $check->equals($this->sourceItem) and $check->getCount() === $this->sourceItem->getCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the item into the target inventory.
|
||||
*
|
||||
* @param Player $source
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function execute(Player $source) : bool{
|
||||
return $this->inventory->setItem($this->inventorySlot, $this->targetItem, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends slot changes to other viewers of the inventory. This will not send any change back to the source Player.
|
||||
*
|
||||
* @param Player $source
|
||||
*/
|
||||
public function onExecuteSuccess(Player $source){
|
||||
$viewers = $this->inventory->getViewers();
|
||||
unset($viewers[spl_object_hash($source)]);
|
||||
$this->inventory->sendSlot($this->inventorySlot, $viewers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the original slot contents to the source player to revert the action.
|
||||
*
|
||||
* @param Player $source
|
||||
*/
|
||||
public function onExecuteFail(Player $source){
|
||||
$this->inventory->sendSlot($this->inventorySlot, $source);
|
||||
}
|
||||
}
|
@ -37,7 +37,6 @@ use pocketmine\network\mcpe\protocol\CommandRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\ContainerClosePacket;
|
||||
use pocketmine\network\mcpe\protocol\CraftingEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\DataPacket;
|
||||
use pocketmine\network\mcpe\protocol\DropItemPacket;
|
||||
use pocketmine\network\mcpe\protocol\EntityEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\EntityFallPacket;
|
||||
use pocketmine\network\mcpe\protocol\EntityPickRequestPacket;
|
||||
@ -164,16 +163,6 @@ class PlayerNetworkSessionAdapter extends NetworkSession{
|
||||
return $this->player->handleAnimate($packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: REMOVE
|
||||
* @param DropItemPacket $packet
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function handleDropItem(DropItemPacket $packet) : bool{
|
||||
return $this->player->handleDropItem($packet);
|
||||
}
|
||||
|
||||
public function handleContainerClose(ContainerClosePacket $packet) : bool{
|
||||
return $this->player->handleContainerClose($packet);
|
||||
}
|
||||
|
@ -1,56 +0,0 @@
|
||||
<?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\network\mcpe\protocol;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
|
||||
/**
|
||||
* @removed
|
||||
*/
|
||||
class DropItemPacket extends DataPacket{
|
||||
const NETWORK_ID = ProtocolInfo::DROP_ITEM_PACKET;
|
||||
|
||||
public $type;
|
||||
/** @var Item */
|
||||
public $item;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->type = $this->getByte();
|
||||
$this->item = $this->getSlot();
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->putByte($this->type);
|
||||
$this->putSlot($this->item);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
return $session->handleDropItem($this);
|
||||
}
|
||||
|
||||
}
|
@ -25,13 +25,18 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\inventory\transaction\action\CreativeInventoryAction;
|
||||
use pocketmine\inventory\transaction\action\DropItemAction;
|
||||
use pocketmine\inventory\transaction\action\InventoryAction;
|
||||
use pocketmine\inventory\transaction\action\SlotChangeAction;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\types\ContainerIds;
|
||||
|
||||
class InventoryTransactionPacket extends DataPacket{
|
||||
const NETWORK_ID = ProtocolInfo::INVENTORY_TRANSACTION_PACKET;
|
||||
|
||||
const TYPE_NORMAL = 0;
|
||||
|
||||
const TYPE_MISMATCH = 1;
|
||||
const TYPE_USE_ITEM = 2;
|
||||
const TYPE_USE_ITEM_ON_ENTITY = 3;
|
||||
const TYPE_RELEASE_ITEM = 4;
|
||||
@ -87,6 +92,7 @@ class InventoryTransactionPacket extends DataPacket{
|
||||
const ACTION_MAGIC_SLOT_CREATIVE_DELETE_ITEM = 0;
|
||||
const ACTION_MAGIC_SLOT_CREATIVE_CREATE_ITEM = 1;
|
||||
|
||||
/** @var InventoryAction[] */
|
||||
public $actions = [];
|
||||
|
||||
public $transactionData;
|
||||
@ -104,7 +110,7 @@ class InventoryTransactionPacket extends DataPacket{
|
||||
|
||||
switch($type){
|
||||
case self::TYPE_NORMAL:
|
||||
case 1:
|
||||
case self::TYPE_MISMATCH:
|
||||
//Regular ComplexInventoryTransaction doesn't read any extra data
|
||||
break;
|
||||
case self::TYPE_USE_ITEM:
|
||||
@ -133,8 +139,6 @@ class InventoryTransactionPacket extends DataPacket{
|
||||
default:
|
||||
throw new \UnexpectedValueException("Unknown transaction type $type");
|
||||
}
|
||||
|
||||
//TODO
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
@ -142,45 +146,58 @@ class InventoryTransactionPacket extends DataPacket{
|
||||
}
|
||||
|
||||
protected function decodeInventoryAction(){
|
||||
$actionBucket = new \stdClass();
|
||||
$actionBucket->inventorySource = $this->decodeInventorySource();
|
||||
$sourceType = $this->getUnsignedVarInt();
|
||||
$containerId = ContainerIds::NONE;
|
||||
$unknown = 0;
|
||||
|
||||
$actionBucket->inventorySlot = $this->getUnsignedVarInt();
|
||||
$actionBucket->oldItem = $this->getSlot();
|
||||
$actionBucket->newItem = $this->getSlot();
|
||||
return $actionBucket;
|
||||
}
|
||||
|
||||
protected function decodeInventorySource(){
|
||||
$bucket = new \stdClass();
|
||||
$bucket->sourceType = $this->getUnsignedVarInt();
|
||||
|
||||
switch($bucket->sourceType){
|
||||
switch($sourceType){
|
||||
case self::SOURCE_CONTAINER:
|
||||
|
||||
$bucket->containerId = $this->getVarInt();
|
||||
$bucket->field_2 = 0;
|
||||
$containerId = $this->getVarInt();
|
||||
break;
|
||||
case 1:
|
||||
break; //unused
|
||||
case self::SOURCE_WORLD:
|
||||
$bucket->containerId = -1;
|
||||
$bucket->field_2 = $this->getUnsignedVarInt();
|
||||
$unknown = $this->getUnsignedVarInt();
|
||||
break;
|
||||
case self::SOURCE_CREATIVE:
|
||||
$bucket->containerId = -1;
|
||||
$bucket->field_2 = 0;
|
||||
break;
|
||||
case self::SOURCE_TODO:
|
||||
$bucket->containerId = $this->getVarInt();
|
||||
$bucket->field_2 = 0;
|
||||
$containerId = $this->getVarInt();
|
||||
break;
|
||||
default:
|
||||
throw new \UnexpectedValueException("Unexpected inventory source type $bucket->sourceType");
|
||||
|
||||
throw new \UnexpectedValueException("Unexpected inventory source type $sourceType");
|
||||
}
|
||||
|
||||
return $bucket;
|
||||
$inventorySlot = $this->getUnsignedVarInt();
|
||||
$sourceItem = $this->getSlot();
|
||||
$targetItem = $this->getSlot();
|
||||
|
||||
switch($sourceType){
|
||||
case self::SOURCE_CONTAINER:
|
||||
if($containerId === ContainerIds::ARMOR){
|
||||
//TODO: HACK!
|
||||
$inventorySlot += 36;
|
||||
$containerId = ContainerIds::INVENTORY;
|
||||
}
|
||||
return new SlotChangeAction($sourceItem, $targetItem, $containerId, $inventorySlot);
|
||||
case self::SOURCE_WORLD:
|
||||
if($inventorySlot !== self::ACTION_MAGIC_SLOT_DROP_ITEM){
|
||||
throw new \UnexpectedValueException("Only expect drop item world action types from client");
|
||||
}
|
||||
|
||||
return new DropItemAction($sourceItem, $targetItem);
|
||||
case self::SOURCE_CREATIVE:
|
||||
if($inventorySlot === self::ACTION_MAGIC_SLOT_CREATIVE_DELETE_ITEM){
|
||||
return new CreativeInventoryAction($sourceItem, $targetItem, CreativeInventoryAction::TYPE_DELETE_ITEM);
|
||||
}elseif($inventorySlot === self::ACTION_MAGIC_SLOT_CREATIVE_CREATE_ITEM){
|
||||
return new CreativeInventoryAction($sourceItem, $targetItem, CreativeInventoryAction::TYPE_CREATE_ITEM);
|
||||
}else{
|
||||
throw new \UnexpectedValueException("Unknown creative inventory action type $inventorySlot");
|
||||
}
|
||||
case self::SOURCE_TODO:
|
||||
return new SlotChangeAction($sourceItem, $targetItem, $containerId, $inventorySlot);
|
||||
default:
|
||||
throw new \UnexpectedValueException("Unknown source type $sourceType");
|
||||
}
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
|
Loading…
x
Reference in New Issue
Block a user