mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-07 10:22:56 +00:00
Cleaned up the InventoryTransactionPacket decoding clusterfuck
@shoghicp, y u do dis... I almost created a sub-packet architecture to deal with this shit :( This mess really ought to be split into multiple packets. Perhaps the PacketPool can be extended to do that in the future.
This commit is contained in:
@ -63,6 +63,11 @@ use pocketmine\network\mcpe\protocol\ShowCreditsPacket;
|
||||
use pocketmine\network\mcpe\protocol\SpawnExperienceOrbPacket;
|
||||
use pocketmine\network\mcpe\protocol\SubClientLoginPacket;
|
||||
use pocketmine\network\mcpe\protocol\TextPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\MismatchTransactionData;
|
||||
use pocketmine\network\mcpe\protocol\types\NormalTransactionData;
|
||||
use pocketmine\network\mcpe\protocol\types\ReleaseItemTransactionData;
|
||||
use pocketmine\network\mcpe\protocol\types\UseItemOnEntityTransactionData;
|
||||
use pocketmine\network\mcpe\protocol\types\UseItemTransactionData;
|
||||
use pocketmine\Player;
|
||||
|
||||
/**
|
||||
@ -112,22 +117,52 @@ class SimpleSessionHandler extends SessionHandler{
|
||||
return true;
|
||||
}
|
||||
|
||||
$result = true;
|
||||
|
||||
if($packet->trData instanceof NormalTransactionData){
|
||||
$result = $this->handleNormalTransaction($packet->trData);
|
||||
}elseif($packet->trData instanceof MismatchTransactionData){
|
||||
$this->player->sendAllInventories();
|
||||
$result = true;
|
||||
}elseif($packet->trData instanceof UseItemTransactionData){
|
||||
$result = $this->handleUseItemTransaction($packet->trData);
|
||||
}elseif($packet->trData instanceof UseItemOnEntityTransactionData){
|
||||
$result = $this->handleUseItemOnEntityTransaction($packet->trData);
|
||||
}elseif($packet->trData instanceof ReleaseItemTransactionData){
|
||||
$result = $this->handleReleaseItemTransaction($packet->trData);
|
||||
}
|
||||
|
||||
if(!$result){
|
||||
$this->player->sendAllInventories();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function handleNormalTransaction(NormalTransactionData $data) : bool{
|
||||
/** @var InventoryAction[] $actions */
|
||||
$actions = [];
|
||||
foreach($packet->actions as $networkInventoryAction){
|
||||
|
||||
$isCrafting = false;
|
||||
$isFinalCraftingPart = false;
|
||||
foreach($data->getActions() as $networkInventoryAction){
|
||||
$isCrafting = $isCrafting || $networkInventoryAction->isCraftingPart();
|
||||
$isFinalCraftingPart = $isFinalCraftingPart || $networkInventoryAction->isFinalCraftingPart();
|
||||
|
||||
try{
|
||||
$action = $networkInventoryAction->createInventoryAction($this->player);
|
||||
if($action !== null){
|
||||
$actions[] = $action;
|
||||
}
|
||||
}catch(\Exception $e){
|
||||
}catch(\UnexpectedValueException $e){
|
||||
$this->player->getServer()->getLogger()->debug("Unhandled inventory action from " . $this->player->getName() . ": " . $e->getMessage());
|
||||
$this->player->sendAllInventories();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if($packet->isCraftingPart){
|
||||
if($isCrafting){
|
||||
//we get the actions for this in several packets, so we need to wait until we have all the pieces before
|
||||
//trying to execute it
|
||||
|
||||
if($this->craftingTransaction === null){
|
||||
$this->craftingTransaction = new CraftingTransaction($this->player, $actions);
|
||||
}else{
|
||||
@ -136,113 +171,98 @@ class SimpleSessionHandler extends SessionHandler{
|
||||
}
|
||||
}
|
||||
|
||||
if($packet->isFinalCraftingPart){
|
||||
//we get the actions for this in several packets, so we need to wait until we have all the pieces before
|
||||
//trying to execute it
|
||||
|
||||
$ret = true;
|
||||
if($isFinalCraftingPart){
|
||||
try{
|
||||
$this->craftingTransaction->execute();
|
||||
}catch(TransactionValidationException $e){
|
||||
$this->player->getServer()->getLogger()->debug("Failed to execute crafting transaction for " . $this->player->getName() . ": " . $e->getMessage());
|
||||
$ret = false;
|
||||
return false;
|
||||
}finally{
|
||||
$this->craftingTransaction = null;
|
||||
}
|
||||
|
||||
}
|
||||
}else{
|
||||
//normal transaction fallthru
|
||||
if($this->craftingTransaction !== null){
|
||||
$this->player->getServer()->getLogger()->debug("Got unexpected normal inventory action with incomplete crafting transaction from " . $this->player->getName() . ", refusing to execute crafting");
|
||||
$this->craftingTransaction = null;
|
||||
return $ret;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
if($this->craftingTransaction !== null){
|
||||
$this->player->getServer()->getLogger()->debug("Got unexpected normal inventory action with incomplete crafting transaction from " . $this->player->getName() . ", refusing to execute crafting");
|
||||
$this->craftingTransaction = null;
|
||||
$transaction = new InventoryTransaction($this->player, $actions);
|
||||
try{
|
||||
$transaction->execute();
|
||||
}catch(TransactionValidationException $e){
|
||||
$logger = $this->player->getServer()->getLogger();
|
||||
$logger->debug("Failed to execute inventory transaction from " . $this->player->getName() . ": " . $e->getMessage());
|
||||
$logger->debug("Actions: " . json_encode($data->getActions()));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//TODO: fix achievement for getting iron from furnace
|
||||
}
|
||||
|
||||
switch($packet->transactionType){
|
||||
case InventoryTransactionPacket::TYPE_NORMAL:
|
||||
$transaction = new InventoryTransaction($this->player, $actions);
|
||||
return true;
|
||||
}
|
||||
|
||||
try{
|
||||
$transaction->execute();
|
||||
}catch(TransactionValidationException $e){
|
||||
$this->player->getServer()->getLogger()->debug("Failed to execute inventory transaction from " . $this->player->getName() . ": " . $e->getMessage());
|
||||
$this->player->getServer()->getLogger()->debug("Actions: " . json_encode($packet->actions));
|
||||
|
||||
return false;
|
||||
private function handleUseItemTransaction(UseItemTransactionData $data) : bool{
|
||||
switch($data->getActionType()){
|
||||
case UseItemTransactionData::ACTION_CLICK_BLOCK:
|
||||
//TODO: start hack for client spam bug
|
||||
$clickPos = $data->getClickPos();
|
||||
$spamBug = ($this->lastRightClickPos !== null and
|
||||
microtime(true) - $this->lastRightClickTime < 0.1 and //100ms
|
||||
$this->lastRightClickPos->distanceSquared($clickPos) < 0.00001 //signature spam bug has 0 distance, but allow some error
|
||||
);
|
||||
//get rid of continued spam if the player clicks and holds right-click
|
||||
$this->lastRightClickPos = clone $clickPos;
|
||||
$this->lastRightClickTime = microtime(true);
|
||||
if($spamBug){
|
||||
return true;
|
||||
}
|
||||
|
||||
//TODO: fix achievement for getting iron from furnace
|
||||
|
||||
//TODO: end hack for client spam bug
|
||||
$this->player->interactBlock($data->getBlockPos(), $data->getFace(), $clickPos);
|
||||
return true;
|
||||
case InventoryTransactionPacket::TYPE_MISMATCH:
|
||||
if(count($packet->actions) > 0){
|
||||
$this->player->getServer()->getLogger()->debug("Expected 0 actions for mismatch, got " . count($packet->actions) . ", " . json_encode($packet->actions));
|
||||
}
|
||||
$this->player->sendAllInventories();
|
||||
|
||||
case UseItemTransactionData::ACTION_BREAK_BLOCK:
|
||||
$this->player->breakBlock($data->getBlockPos());
|
||||
return true;
|
||||
case UseItemTransactionData::ACTION_CLICK_AIR:
|
||||
$this->player->useHeldItem();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function handleUseItemOnEntityTransaction(UseItemOnEntityTransactionData $data) : bool{
|
||||
$target = $this->player->getLevel()->getEntity($data->getEntityRuntimeId());
|
||||
if($target === null){
|
||||
return false;
|
||||
}
|
||||
|
||||
switch($data->getActionType()){
|
||||
case UseItemOnEntityTransactionData::ACTION_INTERACT:
|
||||
$this->player->interactEntity($target, $data->getClickPos());
|
||||
return true;
|
||||
case UseItemOnEntityTransactionData::ACTION_ATTACK:
|
||||
$this->player->attackEntity($target);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function handleReleaseItemTransaction(ReleaseItemTransactionData $data) : bool{
|
||||
switch($data->getActionType()){
|
||||
case ReleaseItemTransactionData::ACTION_RELEASE:
|
||||
$this->player->releaseHeldItem();
|
||||
return true;
|
||||
case ReleaseItemTransactionData::ACTION_CONSUME:
|
||||
$this->player->consumeHeldItem();
|
||||
return true;
|
||||
case InventoryTransactionPacket::TYPE_USE_ITEM:
|
||||
$blockVector = new Vector3($packet->trData->x, $packet->trData->y, $packet->trData->z);
|
||||
$face = $packet->trData->face;
|
||||
|
||||
$type = $packet->trData->actionType;
|
||||
switch($type){
|
||||
case InventoryTransactionPacket::USE_ITEM_ACTION_CLICK_BLOCK:
|
||||
//TODO: start hack for client spam bug
|
||||
$spamBug = ($this->lastRightClickPos !== null and
|
||||
microtime(true) - $this->lastRightClickTime < 0.1 and //100ms
|
||||
$this->lastRightClickPos->distanceSquared($packet->trData->clickPos) < 0.00001 //signature spam bug has 0 distance, but allow some error
|
||||
);
|
||||
//get rid of continued spam if the player clicks and holds right-click
|
||||
$this->lastRightClickPos = clone $packet->trData->clickPos;
|
||||
$this->lastRightClickTime = microtime(true);
|
||||
if($spamBug){
|
||||
return true;
|
||||
}
|
||||
//TODO: end hack for client spam bug
|
||||
$this->player->interactBlock($blockVector, $face, $packet->trData->clickPos);
|
||||
return true;
|
||||
case InventoryTransactionPacket::USE_ITEM_ACTION_BREAK_BLOCK:
|
||||
$this->player->breakBlock($blockVector);
|
||||
return true;
|
||||
case InventoryTransactionPacket::USE_ITEM_ACTION_CLICK_AIR:
|
||||
$this->player->useHeldItem();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case InventoryTransactionPacket::TYPE_USE_ITEM_ON_ENTITY:
|
||||
$target = $this->player->getLevel()->getEntity($packet->trData->entityRuntimeId);
|
||||
if($target === null){
|
||||
return false;
|
||||
}
|
||||
|
||||
switch($packet->trData->actionType){
|
||||
case InventoryTransactionPacket::USE_ITEM_ON_ENTITY_ACTION_INTERACT:
|
||||
$this->player->interactEntity($target, $packet->trData->clickPos);
|
||||
return true;
|
||||
case InventoryTransactionPacket::USE_ITEM_ON_ENTITY_ACTION_ATTACK:
|
||||
$this->player->attackEntity($target);
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
case InventoryTransactionPacket::TYPE_RELEASE_ITEM:
|
||||
switch($packet->trData->actionType){
|
||||
case InventoryTransactionPacket::RELEASE_ITEM_ACTION_RELEASE:
|
||||
$this->player->releaseHeldItem();
|
||||
return true;
|
||||
case InventoryTransactionPacket::RELEASE_ITEM_ACTION_CONSUME:
|
||||
$this->player->consumeHeldItem();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
$this->player->sendAllInventories();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user