Do not assume the presence of a crafting transaction closing marker

fixes #3655, fixes #3241
instead of guessing where the end of the transaction is, we attempt validation after every piece of the transaction, with the assumption being that a crafting transaction will not validate until it's complete.
This commit is contained in:
Dylan K. Taylor 2020-08-02 23:37:33 +01:00
parent 756840f11d
commit 5b620d964e

View File

@ -149,6 +149,7 @@ use pocketmine\network\mcpe\protocol\types\CommandParameter;
use pocketmine\network\mcpe\protocol\types\ContainerIds; use pocketmine\network\mcpe\protocol\types\ContainerIds;
use pocketmine\network\mcpe\protocol\types\DimensionIds; use pocketmine\network\mcpe\protocol\types\DimensionIds;
use pocketmine\network\mcpe\protocol\types\GameMode; use pocketmine\network\mcpe\protocol\types\GameMode;
use pocketmine\network\mcpe\protocol\types\inventory\UIInventorySlotOffset;
use pocketmine\network\mcpe\protocol\types\NetworkInventoryAction; use pocketmine\network\mcpe\protocol\types\NetworkInventoryAction;
use pocketmine\network\mcpe\protocol\types\PersonaPieceTintColor; use pocketmine\network\mcpe\protocol\types\PersonaPieceTintColor;
use pocketmine\network\mcpe\protocol\types\PersonaSkinPiece; use pocketmine\network\mcpe\protocol\types\PersonaSkinPiece;
@ -2392,18 +2393,20 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
/** @var InventoryAction[] $actions */ /** @var InventoryAction[] $actions */
$actions = []; $actions = [];
$isCraftingPart = false; $isCraftingPart = false;
$isFinalCraftingPart = false;
foreach($packet->actions as $networkInventoryAction){ foreach($packet->actions as $networkInventoryAction){
if( if(
$networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_TODO and ( $networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_TODO and (
$networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_RESULT or $networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_RESULT or
$networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_USE_INGREDIENT $networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_USE_INGREDIENT
) or (
$this->craftingTransaction !== null &&
!$networkInventoryAction->oldItem->equalsExact($networkInventoryAction->newItem) &&
$networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_CONTAINER &&
$networkInventoryAction->windowId === ContainerIds::UI &&
$networkInventoryAction->inventorySlot === UIInventorySlotOffset::CREATED_ITEM_OUTPUT
) )
){ ){
$isCraftingPart = true; $isCraftingPart = true;
if($networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_RESULT){
$isFinalCraftingPart = true;
}
} }
try{ try{
$action = $networkInventoryAction->createInventoryAction($this); $action = $networkInventoryAction->createInventoryAction($this);
@ -2426,23 +2429,24 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
} }
} }
if($isFinalCraftingPart){ try{
//we get the actions for this in several packets, so we need to wait until we have all the pieces before $this->craftingTransaction->validate();
//trying to execute it }catch(TransactionValidationException $e){
//transaction is incomplete - crafting transaction comes in lots of little bits, so we have to collect
$ret = true; //all of the parts before we can execute it
try{ return true;
$this->craftingTransaction->execute();
}catch(TransactionValidationException $e){
$this->server->getLogger()->debug("Failed to execute crafting transaction for " . $this->getName() . ": " . $e->getMessage());
$ret = false;
}
$this->craftingTransaction = null;
return $ret;
} }
return true; $ret = true;
try{
$this->craftingTransaction->execute();
}catch(TransactionValidationException $e){
$this->server->getLogger()->debug("Failed to execute crafting transaction for " . $this->getName() . ": " . $e->getMessage());
$ret = false;
}
$this->craftingTransaction = null;
return $ret;
}elseif($this->craftingTransaction !== null){ }elseif($this->craftingTransaction !== null){
$this->server->getLogger()->debug("Got unexpected normal inventory action with incomplete crafting transaction from " . $this->getName() . ", refusing to execute crafting"); $this->server->getLogger()->debug("Got unexpected normal inventory action with incomplete crafting transaction from " . $this->getName() . ", refusing to execute crafting");
$this->craftingTransaction = null; $this->craftingTransaction = null;