Merge branch 'minor-next' into major-next

This commit is contained in:
Dylan K. Taylor 2023-08-18 12:33:54 +01:00
commit 5c915a3dfe
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
15 changed files with 401 additions and 36 deletions

View File

@ -24,14 +24,33 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\SupportType;
use pocketmine\entity\Entity;
use pocketmine\event\block\PressurePlateUpdateEvent;
use pocketmine\item\Item;
use pocketmine\math\Axis;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use pocketmine\world\sound\PressurePlateActivateSound;
use pocketmine\world\sound\PressurePlateDeactivateSound;
use function count;
abstract class PressurePlate extends Transparent{
private readonly int $deactivationDelayTicks;
public function __construct(
BlockIdentifier $idInfo,
string $name,
BlockTypeInfo $typeInfo,
int $deactivationDelayTicks = 20 //TODO: make this mandatory in PM6
){
parent::__construct($idInfo, $name, $typeInfo);
$this->deactivationDelayTicks = $deactivationDelayTicks;
}
public function isSolid() : bool{
return false;
}
@ -61,5 +80,89 @@ abstract class PressurePlate extends Transparent{
}
}
//TODO
public function hasEntityCollision() : bool{
return true;
}
public function onEntityInside(Entity $entity) : bool{
if(!$this->hasOutputSignal()){
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, 0);
}
return true;
}
/**
* Returns the AABB that entities must intersect to activate the pressure plate.
* Note that this is not the same as the collision box (pressure plate doesn't have one), nor the visual bounding
* box. The activation area has a height of 0.25 blocks.
*/
protected function getActivationBox() : AxisAlignedBB{
return AxisAlignedBB::one()
->squash(Axis::X, 1 / 8)
->squash(Axis::Z, 1 / 8)
->trim(Facing::UP, 3 / 4)
->offset($this->position->x, $this->position->y, $this->position->z);
}
/**
* TODO: make this abstract in PM6
*/
protected function hasOutputSignal() : bool{
return false;
}
/**
* TODO: make this abstract in PM6
*
* @param Entity[] $entities
*
* @return mixed[]
* @phpstan-return array{Block, ?bool}
*/
protected function calculatePlateState(array $entities) : array{
return [$this, null];
}
/**
* Filters entities which don't affect the pressure plate state from the given list.
*
* @param Entity[] $entities
* @return Entity[]
*/
protected function filterIrrelevantEntities(array $entities) : array{
return $entities;
}
public function onScheduledUpdate() : void{
$world = $this->position->getWorld();
$intersectionAABB = $this->getActivationBox();
$activatingEntities = $this->filterIrrelevantEntities($world->getNearbyEntities($intersectionAABB));
//if an irrelevant entity is inside the full cube space of the pressure plate but not activating the plate,
//it will cause scheduled updates on the plate every tick. We don't want to fire events in this case if the
//plate is already deactivated.
if(count($activatingEntities) > 0 || $this->hasOutputSignal()){
[$newState, $pressedChange] = $this->calculatePlateState($activatingEntities);
//always call this, in case there are new entities on the plate
if(PressurePlateUpdateEvent::hasHandlers()){
$ev = new PressurePlateUpdateEvent($this, $newState, $activatingEntities);
$ev->call();
$newState = $ev->isCancelled() ? null : $ev->getNewState();
}
if($newState !== null){
$world->setBlock($this->position, $newState);
if($pressedChange !== null){
$world->addSound($this->position, $pressedChange ?
new PressurePlateActivateSound($this) :
new PressurePlateDeactivateSound($this)
);
}
}
if($pressedChange ?? $this->hasOutputSignal()){
$world->scheduleDelayedBlockUpdate($this->position, $this->deactivationDelayTicks);
}
}
}
}

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\data\runtime\RuntimeDataDescriber;
use function count;
abstract class SimplePressurePlate extends PressurePlate{
protected bool $pressed = false;
@ -39,4 +40,19 @@ abstract class SimplePressurePlate extends PressurePlate{
$this->pressed = $pressed;
return $this;
}
protected function hasOutputSignal() : bool{
return $this->pressed;
}
protected function calculatePlateState(array $entities) : array{
$newPressed = count($entities) > 0;
if($newPressed === $this->pressed){
return [$this, null];
}
return [
(clone $this)->setPressed($newPressed),
$newPressed
];
}
}

View File

@ -23,6 +23,13 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\entity\Entity;
use pocketmine\entity\Living;
use function array_filter;
class StonePressurePlate extends SimplePressurePlate{
protected function filterIrrelevantEntities(array $entities) : array{
return array_filter($entities, fn(Entity $e) => $e instanceof Living); //TODO: armor stands should activate stone plates too
}
}

View File

@ -1114,8 +1114,20 @@ final class VanillaBlocks{
self::register("lily_pad", new WaterLily(new BID(Ids::LILY_PAD), "Lily Pad", new Info(BreakInfo::instant())));
$weightedPressurePlateBreakInfo = new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD()));
self::register("weighted_pressure_plate_heavy", new WeightedPressurePlateHeavy(new BID(Ids::WEIGHTED_PRESSURE_PLATE_HEAVY), "Weighted Pressure Plate Heavy", $weightedPressurePlateBreakInfo));
self::register("weighted_pressure_plate_light", new WeightedPressurePlateLight(new BID(Ids::WEIGHTED_PRESSURE_PLATE_LIGHT), "Weighted Pressure Plate Light", $weightedPressurePlateBreakInfo));
self::register("weighted_pressure_plate_heavy", new WeightedPressurePlateHeavy(
new BID(Ids::WEIGHTED_PRESSURE_PLATE_HEAVY),
"Weighted Pressure Plate Heavy",
$weightedPressurePlateBreakInfo,
deactivationDelayTicks: 10,
signalStrengthFactor: 0.1
));
self::register("weighted_pressure_plate_light", new WeightedPressurePlateLight(
new BID(Ids::WEIGHTED_PRESSURE_PLATE_LIGHT),
"Weighted Pressure Plate Light",
$weightedPressurePlateBreakInfo,
deactivationDelayTicks: 10,
signalStrengthFactor: 1.0
));
self::register("wheat", new Wheat(new BID(Ids::WHEAT), "Wheat Block", new Info(BreakInfo::instant())));
$leavesBreakInfo = new Info(new class(0.2, ToolType::HOE) extends BreakInfo{
@ -1266,7 +1278,7 @@ final class VanillaBlocks{
self::register($idName("door"), new WoodenDoor(WoodLikeBlockIdHelper::getDoorIdentifier($woodType), $name . " Door", $woodenDoorBreakInfo, $woodType));
self::register($idName("button"), new WoodenButton(WoodLikeBlockIdHelper::getButtonIdentifier($woodType), $name . " Button", $woodenButtonBreakInfo, $woodType));
self::register($idName("pressure_plate"), new WoodenPressurePlate(WoodLikeBlockIdHelper::getPressurePlateIdentifier($woodType), $name . " Pressure Plate", $woodenPressurePlateBreakInfo, $woodType));
self::register($idName("pressure_plate"), new WoodenPressurePlate(WoodLikeBlockIdHelper::getPressurePlateIdentifier($woodType), $name . " Pressure Plate", $woodenPressurePlateBreakInfo, $woodType, 20));
self::register($idName("trapdoor"), new WoodenTrapdoor(WoodLikeBlockIdHelper::getTrapdoorIdentifier($woodType), $name . " Trapdoor", $woodenDoorBreakInfo, $woodType));
[$floorSignId, $wallSignId, $signAsItem] = WoodLikeBlockIdHelper::getSignInfo($woodType);
@ -1491,7 +1503,7 @@ final class VanillaBlocks{
$prefix = fn(string $thing) => "Polished Blackstone" . ($thing !== "" ? " $thing" : "");
self::register("polished_blackstone", new Opaque(new BID(Ids::POLISHED_BLACKSTONE), $prefix(""), $blackstoneBreakInfo));
self::register("polished_blackstone_button", new StoneButton(new BID(Ids::POLISHED_BLACKSTONE_BUTTON), $prefix("Button"), new Info(BreakInfo::pickaxe(0.5))));
self::register("polished_blackstone_pressure_plate", new StonePressurePlate(new BID(Ids::POLISHED_BLACKSTONE_PRESSURE_PLATE), $prefix("Pressure Plate"), new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD()))));
self::register("polished_blackstone_pressure_plate", new StonePressurePlate(new BID(Ids::POLISHED_BLACKSTONE_PRESSURE_PLATE), $prefix("Pressure Plate"), new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD())), 20));
self::register("polished_blackstone_slab", new Slab(new BID(Ids::POLISHED_BLACKSTONE_SLAB), $prefix(""), $slabBreakInfo));
self::register("polished_blackstone_stairs", new Stair(new BID(Ids::POLISHED_BLACKSTONE_STAIRS), $prefix("Stairs"), $blackstoneBreakInfo));
self::register("polished_blackstone_wall", new Wall(new BID(Ids::POLISHED_BLACKSTONE_WALL), $prefix("Wall"), $blackstoneBreakInfo));

View File

@ -24,7 +24,40 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait;
use function ceil;
use function count;
use function max;
use function min;
abstract class WeightedPressurePlate extends PressurePlate{
class WeightedPressurePlate extends PressurePlate{
use AnalogRedstoneSignalEmitterTrait;
private readonly float $signalStrengthFactor;
/**
* @param float $signalStrengthFactor Number of entities on the plate is divided by this value to get signal strength
*/
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo, int $deactivationDelayTicks, float $signalStrengthFactor = 1.0){
parent::__construct($idInfo, $name, $typeInfo, $deactivationDelayTicks);
$this->signalStrengthFactor = $signalStrengthFactor;
}
protected function hasOutputSignal() : bool{
return $this->signalStrength > 0;
}
protected function calculatePlateState(array $entities) : array{
$newSignalStrength = min(15, max(0,
(int) ceil(count($entities) * $this->signalStrengthFactor)
));
if($newSignalStrength === $this->signalStrength){
return [$this, null];
}
$wasActive = $this->signalStrength !== 0;
$isActive = $newSignalStrength !== 0;
return [
(clone $this)->setOutputSignalStrength($newSignalStrength),
$wasActive !== $isActive ? $isActive : null
];
}
}

View File

@ -23,6 +23,9 @@ declare(strict_types=1);
namespace pocketmine\block;
/**
* @deprecated
*/
class WeightedPressurePlateHeavy extends WeightedPressurePlate{
}

View File

@ -23,6 +23,9 @@ declare(strict_types=1);
namespace pocketmine\block;
/**
* @deprecated
*/
class WeightedPressurePlateLight extends WeightedPressurePlate{
}

View File

@ -23,11 +23,23 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\WoodType;
use pocketmine\block\utils\WoodTypeTrait;
class WoodenPressurePlate extends SimplePressurePlate{
use WoodTypeTrait;
public function __construct(
BlockIdentifier $idInfo,
string $name,
BlockTypeInfo $typeInfo,
WoodType $woodType,
int $deactivationDelayTicks = 20 //TODO: make this mandatory in PM6
){
$this->woodType = $woodType;
parent::__construct($idInfo, $name, $typeInfo, $deactivationDelayTicks);
}
public function getFuelTime() : int{
return 300;
}

View File

@ -25,6 +25,7 @@ namespace pocketmine\command\defaults;
use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\item\enchantment\EnchantmentHelper;
use pocketmine\item\enchantment\EnchantmentInstance;
use pocketmine\item\enchantment\StringToEnchantmentParser;
use pocketmine\lang\KnownTranslationFactory;
@ -76,8 +77,9 @@ class EnchantCommand extends VanillaCommand{
}
}
$item->addEnchantment(new EnchantmentInstance($enchantment, $level));
$player->getInventory()->setItemInHand($item);
//this is necessary to deal with enchanted books, which are a different item type than regular books
$enchantedItem = EnchantmentHelper::enchantItem($item, [new EnchantmentInstance($enchantment, $level)]);
$player->getInventory()->setItemInHand($enchantedItem);
self::broadcastCommandMessage($sender, KnownTranslationFactory::commands_enchant_success($player->getName()));
return true;

View File

@ -0,0 +1,53 @@
<?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\event\block;
use pocketmine\block\Block;
use pocketmine\entity\Entity;
use pocketmine\event\Cancellable;
/**
* Called whenever the list of entities on a pressure plate changes.
* Depending on the type of pressure plate, this might turn on/off its signal, or change the signal strength.
*/
final class PressurePlateUpdateEvent extends BaseBlockChangeEvent implements Cancellable{
/**
* @param Entity[] $activatingEntities
*/
public function __construct(
Block $block,
Block $newState,
private array $activatingEntities
){
parent::__construct($block, $newState);
}
/**
* Returns a list of entities intersecting the pressure plate's activation box.
* If the pressure plate is about to deactivate, this list will be empty.
*
* @return Entity[]
*/
public function getActivatingEntities() : array{ return $this->activatingEntities; }
}

View File

@ -31,6 +31,7 @@ use pocketmine\item\ItemTypeIds;
use pocketmine\player\Player;
use pocketmine\utils\AssumptionFailedError;
use function count;
use function min;
class EnchantTransaction extends InventoryTransaction{
@ -67,9 +68,8 @@ class EnchantTransaction extends InventoryTransaction{
if($xpLevel < $requiredXpLevel){
throw new TransactionValidationException("Player's XP level $xpLevel is less than the required XP level $requiredXpLevel");
}
if($xpLevel < $this->cost){
throw new TransactionValidationException("Player's XP level $xpLevel is less than the XP level cost $this->cost");
}
//XP level cost is intentionally not checked here, as the required level may be lower than the cost, allowing
//the option to be used with less XP than the cost - in this case, as much XP as possible will be deducted.
}
public function validate() : void{
@ -115,7 +115,9 @@ class EnchantTransaction extends InventoryTransaction{
parent::execute();
if($this->source->hasFiniteResources()){
$this->source->getXpManager()->subtractXpLevels($this->cost);
//If the required XP level is less than the XP cost, the option can be selected with less XP than the cost.
//In this case, as much XP as possible will be taken.
$this->source->getXpManager()->subtractXpLevels(min($this->cost, $this->source->getXpManager()->getXpLevel()));
}
$this->source->setEnchantmentSeed($this->source->generateEnchantmentSeed());
}

View File

@ -431,7 +431,7 @@ final class VanillaItems{
self::register("echo_shard", new Item(new IID(Ids::ECHO_SHARD), "Echo Shard"));
self::register("egg", new Egg(new IID(Ids::EGG), "Egg"));
self::register("emerald", new Item(new IID(Ids::EMERALD), "Emerald"));
self::register("enchanted_book", new EnchantedBook(new IID(Ids::ENCHANTED_BOOK), "Enchanted Book"));
self::register("enchanted_book", new EnchantedBook(new IID(Ids::ENCHANTED_BOOK), "Enchanted Book", [EnchantmentTags::ALL]));
self::register("enchanted_golden_apple", new GoldenAppleEnchanted(new IID(Ids::ENCHANTED_GOLDEN_APPLE), "Enchanted Golden Apple"));
self::register("ender_pearl", new EnderPearl(new IID(Ids::ENDER_PEARL), "Ender Pearl"));
self::register("experience_bottle", new ExperienceBottle(new IID(Ids::EXPERIENCE_BOTTLE), "Bottle o' Enchanting"));

View File

@ -63,6 +63,7 @@ use pocketmine\network\PacketHandlingException;
use pocketmine\player\Player;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\ObjectSet;
use function array_fill_keys;
use function array_keys;
use function array_map;
use function array_search;
@ -444,6 +445,50 @@ class InventoryManager{
unset($inventoryEntry->predictions[$slot]);
}
private function sendInventorySlotPackets(int $windowId, int $netSlot, ItemStackWrapper $itemStackWrapper) : void{
/*
* TODO: HACK!
* As of 1.20.12, the client ignores change of itemstackID in some cases when the old item == the new item.
* Notably, this happens with armor, offhand and enchanting tables, but not with main inventory.
* While we could track the items previously sent to the client, that's a waste of memory and would
* cost performance. Instead, clear the slot(s) first, then send the new item(s).
* The network cost of doing this is fortunately minimal, as an air itemstack is only 1 byte.
*/
if($itemStackWrapper->getStackId() !== 0){
$this->session->sendDataPacket(InventorySlotPacket::create(
$windowId,
$netSlot,
new ItemStackWrapper(0, ItemStack::null())
));
}
//now send the real contents
$this->session->sendDataPacket(InventorySlotPacket::create(
$windowId,
$netSlot,
$itemStackWrapper
));
}
/**
* @param ItemStackWrapper[] $itemStackWrappers
*/
private function sendInventoryContentPackets(int $windowId, array $itemStackWrappers) : void{
/*
* TODO: HACK!
* As of 1.20.12, the client ignores change of itemstackID in some cases when the old item == the new item.
* Notably, this happens with armor, offhand and enchanting tables, but not with main inventory.
* While we could track the items previously sent to the client, that's a waste of memory and would
* cost performance. Instead, clear the slot(s) first, then send the new item(s).
* The network cost of doing this is fortunately minimal, as an air itemstack is only 1 byte.
*/
$this->session->sendDataPacket(InventoryContentPacket::create(
$windowId,
array_fill_keys(array_keys($itemStackWrappers), new ItemStackWrapper(0, ItemStack::null()))
));
//now send the real contents
$this->session->sendDataPacket(InventoryContentPacket::create($windowId, $itemStackWrappers));
}
public function syncSlot(Inventory $inventory, int $slot, ItemStack $itemStack) : void{
$entry = $this->inventories[spl_object_id($inventory)] ?? null;
if($entry === null){
@ -468,24 +513,9 @@ class InventoryManager{
//This can cause a lot of problems (totems, arrows, and more...).
//The workaround is to send an InventoryContentPacket instead
//BDS (Bedrock Dedicated Server) also seems to work this way.
$this->session->sendDataPacket(InventoryContentPacket::create($windowId, [$itemStackWrapper]));
$this->sendInventoryContentPackets($windowId, [$itemStackWrapper]);
}else{
if($windowId === ContainerIds::ARMOR){
//TODO: HACK!
//When right-clicking to equip armour, the client predicts the content of the armour slot, but
//doesn't report it in the transaction packet. The server then sends an InventorySlotPacket to
//the client, assuming the slot changed for some other reason, since there is no prediction for
//the slot.
//However, later requests involving that itemstack will refer to the request ID in which the
//armour was equipped, instead of the stack ID provided by the server in the outgoing
//InventorySlotPacket. (Perhaps because the item is already the same as the client actually
//predicted, but didn't tell us?)
//We work around this bug by setting the slot to air and then back to the correct item. In
//theory, setting a different count and then back again (or changing any other property) would
//also work, but this is simpler.
$this->session->sendDataPacket(InventorySlotPacket::create($windowId, $netSlot, new ItemStackWrapper(0, ItemStack::null())));
}
$this->session->sendDataPacket(InventorySlotPacket::create($windowId, $netSlot, $itemStackWrapper));
$this->sendInventorySlotPackets($windowId, $netSlot, $itemStackWrapper);
}
unset($entry->predictions[$slot], $entry->pendingSyncs[$slot]);
}
@ -512,20 +542,17 @@ class InventoryManager{
$info = $this->trackItemStack($entry, $slot, $itemStack, null);
$contents[] = new ItemStackWrapper($info->getStackId(), $itemStack);
}
$clearSlotWrapper = new ItemStackWrapper(0, ItemStack::null());
if($entry->complexSlotMap !== null){
foreach($contents as $slotId => $info){
$packetSlot = $entry->complexSlotMap->mapCoreToNet($slotId) ?? null;
if($packetSlot === null){
continue;
}
$this->session->sendDataPacket(InventorySlotPacket::create(
$windowId,
$packetSlot,
$info
));
$this->sendInventorySlotPackets($windowId, $packetSlot, $info);
}
}else{
$this->session->sendDataPacket(InventoryContentPacket::create($windowId, $contents));
$this->sendInventoryContentPackets($windowId, $contents);
}
}
}

View File

@ -0,0 +1,46 @@
<?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\world\sound;
use pocketmine\block\Block;
use pocketmine\math\Vector3;
use pocketmine\network\mcpe\convert\TypeConverter;
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
use pocketmine\network\mcpe\protocol\types\LevelSoundEvent;
final class PressurePlateActivateSound implements Sound{
public function __construct(
private readonly Block $block
){}
public function encode(Vector3 $pos) : array{
return [LevelSoundEventPacket::nonActorSound(
LevelSoundEvent::PRESSURE_PLATE_CLICK_ON,
$pos,
false,
TypeConverter::getInstance()->getBlockTranslator()->internalIdToNetworkId($this->block->getStateId())
)];
}
}

View File

@ -0,0 +1,46 @@
<?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\world\sound;
use pocketmine\block\Block;
use pocketmine\math\Vector3;
use pocketmine\network\mcpe\convert\TypeConverter;
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
use pocketmine\network\mcpe\protocol\types\LevelSoundEvent;
final class PressurePlateDeactivateSound implements Sound{
public function __construct(
private readonly Block $block
){}
public function encode(Vector3 $pos) : array{
return [LevelSoundEventPacket::nonActorSound(
LevelSoundEvent::PRESSURE_PLATE_CLICK_OFF,
$pos,
false,
TypeConverter::getInstance()->getBlockTranslator()->internalIdToNetworkId($this->block->getStateId())
)];
}
}