Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/event/player/PlayerInteractEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ class PlayerInteractEvent extends PlayerEvent implements Cancellable{

protected Vector3 $touchVector;

protected bool $useItem = true;
protected bool $useBlock = true;

public function __construct(
Player $player,
protected Item $item,
Expand Down Expand Up @@ -73,4 +76,28 @@ public function getTouchVector() : Vector3{
public function getFace() : int{
return $this->blockFace;
}

/**
* Returns whether the item may react to the interaction. If disabled, items such as spawn eggs will not activate.
* This does NOT prevent blocks from being placed - it makes the item behave as if the player is sneaking.
*/
public function useItem() : bool{ return $this->useItem; }

/**
* Sets whether the used item may react to the interaction. If false, items such as spawn eggs will not activate.
* This does NOT prevent blocks from being placed - it makes the item behave as if the player is sneaking.
*/
public function setUseItem(bool $useItem) : void{ $this->useItem = $useItem; }

/**
* Returns whether the block may react to the interaction. If false, doors, fence gates and trapdoors will not
* respond, containers will not open, etc.
*/
public function useBlock() : bool{ return $this->useBlock; }

/**
* Sets whether the block may react to the interaction. If false, doors, fence gates and trapdoors will not
* respond, containers will not open, etc.
*/
public function setUseBlock(bool $useBlock) : void{ $this->useBlock = $useBlock; }
}
19 changes: 11 additions & 8 deletions src/network/mcpe/handler/InGamePacketHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -493,15 +493,18 @@ private function handleUseItemTransaction(UseItemTransactionData $data) : bool{

$blockPos = $data->getBlockPosition();
$vBlockPos = new Vector3($blockPos->getX(), $blockPos->getY(), $blockPos->getZ());
if(!$this->player->interactBlock($vBlockPos, $data->getFace(), $clickPos)){
$this->onFailedBlockAction($vBlockPos, $data->getFace());
}
$this->player->interactBlock($vBlockPos, $data->getFace(), $clickPos);
//always sync this in case plugins caused a different result than the client expected
//we *could* try to enhance detection of plugin-altered behaviour, but this would require propagating
//more information up the stack. For now I think this is good enough.
//if only the client would tell us what blocks it thinks changed...
$this->syncBlocksNearby($vBlockPos, $data->getFace());
return true;
case UseItemTransactionData::ACTION_BREAK_BLOCK:
$blockPos = $data->getBlockPosition();
$vBlockPos = new Vector3($blockPos->getX(), $blockPos->getY(), $blockPos->getZ());
if(!$this->player->breakBlock($vBlockPos)){
$this->onFailedBlockAction($vBlockPos, null);
$this->syncBlocksNearby($vBlockPos, null);
}
return true;
case UseItemTransactionData::ACTION_CLICK_AIR:
Expand Down Expand Up @@ -529,9 +532,9 @@ private static function validateFacing(int $facing) : void{
}

/**
* Internal function used to execute rollbacks when an action fails on a block.
* Syncs blocks nearby to ensure that the client and server agree on the world's blocks after a block interaction.
*/
private function onFailedBlockAction(Vector3 $blockPos, ?int $face) : void{
private function syncBlocksNearby(Vector3 $blockPos, ?int $face) : void{
if($blockPos->distanceSquared($this->player->getLocation()) < 10000){
$blocks = $blockPos->sidesArray();
if($face !== null){
Expand Down Expand Up @@ -682,7 +685,7 @@ private function handlePlayerActionFromData(int $action, BlockPosition $blockPos
case PlayerAction::START_BREAK:
self::validateFacing($face);
if(!$this->player->attackBlock($pos, $face)){
$this->onFailedBlockAction($pos, $face);
$this->syncBlocksNearby($pos, $face);
}

break;
Expand Down Expand Up @@ -998,7 +1001,7 @@ public function handleLecternUpdate(LecternUpdatePacket $packet) : bool{
$lectern = $world->getBlockAt($pos->getX(), $pos->getY(), $pos->getZ());
if($lectern instanceof Lectern && $this->player->canInteract($lectern->getPosition(), 15)){
if(!$lectern->onPageTurn($packet->page)){
$this->onFailedBlockAction($lectern->getPosition(), null);
$this->syncBlocksNearby($lectern->getPosition(), null);
}
return true;
}
Expand Down
14 changes: 10 additions & 4 deletions src/world/World.php
Original file line number Diff line number Diff line change
Expand Up @@ -2173,19 +2173,25 @@ public function useItemOn(Vector3 $vector, Item &$item, int $face, ?Vector3 $cli

if($player !== null){
$ev = new PlayerInteractEvent($player, $item, $blockClicked, $clickVector, $face, PlayerInteractEvent::RIGHT_CLICK_BLOCK);
if($player->isSneaking()){
$ev->setUseItem(false);
$ev->setUseBlock($item->isNull()); //opening doors is still possible when sneaking if using an empty hand
}
if($player->isSpectator()){
$ev->cancel(); //set it to cancelled so plugins can bypass this
}

$ev->call();
if(!$ev->isCancelled()){
if((!$player->isSneaking() || $item->isNull()) && $blockClicked->onInteract($item, $face, $clickVector, $player, $returnedItems)){
if($ev->useBlock() && $blockClicked->onInteract($item, $face, $clickVector, $player, $returnedItems)){
return true;
}

$result = $item->onInteractBlock($player, $blockReplace, $blockClicked, $face, $clickVector, $returnedItems);
if($result !== ItemUseResult::NONE){
return $result === ItemUseResult::SUCCESS;
if($ev->useItem()){
$result = $item->onInteractBlock($player, $blockReplace, $blockClicked, $face, $clickVector, $returnedItems);
if($result !== ItemUseResult::NONE){
return $result === ItemUseResult::SUCCESS;
}
}
}else{
return false;
Expand Down