mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-20 16:00:20 +00:00
Add Shulker Boxes (#3678)
this implementation is working, although incomplete: - The shulker close sound should not be played until the end of the shulker closing animation, which takes approximately 1 second. - An open shulker box has a different collision box than a closed one - it should be +0.5 in whichever direction the shulker is facing. (During the animation, the bounding box also dynamically changes size - you can see this in vanilla by shooting an arrow into the top of an open shulkerbox facing UP, and then closing it - the arrow will fall and collide with the lid multiple times. However, resolving both of these issues requires significant internal changes which are beyond the scope of this PR.
This commit is contained in:
parent
3997e612b1
commit
fc090e238d
@ -44,6 +44,7 @@ use pocketmine\block\tile\ItemFrame as TileItemFrame;
|
||||
use pocketmine\block\tile\Jukebox as TileJukebox;
|
||||
use pocketmine\block\tile\MonsterSpawner as TileMonsterSpawner;
|
||||
use pocketmine\block\tile\Note as TileNote;
|
||||
use pocketmine\block\tile\ShulkerBox as TileShulkerBox;
|
||||
use pocketmine\block\tile\Skull as TileSkull;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\utils\InvalidBlockStateException;
|
||||
@ -316,6 +317,8 @@ class BlockFactory{
|
||||
$this->register(new SnowLayer(new BID(Ids::SNOW_LAYER, 0), "Snow Layer", new BlockBreakInfo(0.1, BlockToolType::SHOVEL, ToolTier::WOOD()->getHarvestLevel())));
|
||||
$this->register(new SoulSand(new BID(Ids::SOUL_SAND, 0), "Soul Sand", new BlockBreakInfo(0.5, BlockToolType::SHOVEL)));
|
||||
$this->register(new Sponge(new BID(Ids::SPONGE, 0), "Sponge", new BlockBreakInfo(0.6, BlockToolType::HOE)));
|
||||
$shulkerBoxBreakInfo = new BlockBreakInfo(2, BlockToolType::PICKAXE);
|
||||
$this->register(new ShulkerBox(new BID(Ids::UNDYED_SHULKER_BOX, 0, null, TileShulkerBox::class), "Shulker Box", $shulkerBoxBreakInfo));
|
||||
|
||||
$stoneBreakInfo = new BlockBreakInfo(1.5, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0);
|
||||
$this->register($stone = new class(new BID(Ids::STONE, Meta::STONE_NORMAL), "Stone", $stoneBreakInfo) extends Opaque{
|
||||
@ -473,6 +476,7 @@ class BlockFactory{
|
||||
};
|
||||
$this->register(new GlazedTerracotta(BlockLegacyIdHelper::getGlazedTerracottaIdentifier($color), $coloredName("Glazed Terracotta"), $glazedTerracottaBreakInfo));
|
||||
}
|
||||
$this->register(new DyedShulkerBox(new BID(Ids::SHULKER_BOX, 0, null, TileShulkerBox::class), "Dyed Shulker Box", $shulkerBoxBreakInfo));
|
||||
$this->register(new StainedGlass(new BID(Ids::STAINED_GLASS, 0), "Stained Glass", $glassBreakInfo));
|
||||
$this->register(new StainedGlassPane(new BID(Ids::STAINED_GLASS_PANE, 0), "Stained Glass Pane", $glassBreakInfo));
|
||||
$this->register(new StainedHardenedClay(new BID(Ids::STAINED_CLAY, 0), "Stained Clay", $hardenedClayBreakInfo));
|
||||
@ -569,7 +573,6 @@ class BlockFactory{
|
||||
//TODO: minecraft:repeating_command_block
|
||||
//TODO: minecraft:scaffolding
|
||||
//TODO: minecraft:seagrass
|
||||
//TODO: minecraft:shulker_box
|
||||
//TODO: minecraft:slime
|
||||
//TODO: minecraft:smithing_table
|
||||
//TODO: minecraft:smoker
|
||||
@ -578,7 +581,6 @@ class BlockFactory{
|
||||
//TODO: minecraft:structure_block
|
||||
//TODO: minecraft:sweet_berry_bush
|
||||
//TODO: minecraft:turtle_egg
|
||||
//TODO: minecraft:undyed_shulker_box
|
||||
//endregion
|
||||
|
||||
//region --- auto-generated TODOs for bedrock-1.13.0 ---
|
||||
|
36
src/block/DyedShulkerBox.php
Normal file
36
src/block/DyedShulkerBox.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?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\block;
|
||||
|
||||
use pocketmine\block\utils\ColorInMetadataTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
|
||||
final class DyedShulkerBox extends ShulkerBox{
|
||||
use ColorInMetadataTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
|
||||
$this->color = DyeColor::WHITE();
|
||||
parent::__construct($idInfo, $name, $breakInfo);
|
||||
}
|
||||
}
|
95
src/block/ShulkerBox.php
Normal file
95
src/block/ShulkerBox.php
Normal file
@ -0,0 +1,95 @@
|
||||
<?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\block;
|
||||
|
||||
use pocketmine\block\tile\ShulkerBox as TileShulkerBox;
|
||||
use pocketmine\block\utils\AnyFacingTrait;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
class ShulkerBox extends Opaque{
|
||||
use AnyFacingTrait;
|
||||
|
||||
public function writeStateToWorld() : void{
|
||||
parent::writeStateToWorld();
|
||||
$shulker = $this->pos->getWorld()->getTile($this->pos);
|
||||
if($shulker instanceof TileShulkerBox){
|
||||
$shulker->setFacing($this->facing);
|
||||
}
|
||||
}
|
||||
|
||||
public function readStateFromWorld() : void{
|
||||
parent::readStateFromWorld();
|
||||
$shulker = $this->pos->getWorld()->getTile($this->pos);
|
||||
if($shulker instanceof TileShulkerBox){
|
||||
$this->facing = $shulker->getFacing();
|
||||
}
|
||||
}
|
||||
|
||||
public function getMaxStackSize() : int{
|
||||
return 1;
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$this->facing = $face;
|
||||
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function asItem() : Item{
|
||||
$item = parent::asItem();
|
||||
$shulker = $this->pos->getWorld()->getTile($this->pos);
|
||||
if($shulker instanceof TileShulkerBox){
|
||||
$shulkerNBT = $shulker->getCleanedNBT();
|
||||
if($shulkerNBT !== null){
|
||||
$item->setNamedTag($shulkerNBT);
|
||||
}
|
||||
if($shulker->hasName()){
|
||||
$item->setCustomName($shulker->getName());
|
||||
}
|
||||
}
|
||||
return $item;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($player instanceof Player){
|
||||
|
||||
$shulker = $this->pos->getWorld()->getTile($this->pos);
|
||||
if($shulker instanceof TileShulkerBox){
|
||||
if(
|
||||
$this->getSide($this->facing)->getId() !== BlockLegacyIds::AIR or
|
||||
!$shulker->canOpenWith($item->getCustomName())
|
||||
){
|
||||
return true;
|
||||
}
|
||||
|
||||
$player->setCurrentWindow($shulker->getInventory());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -161,6 +161,7 @@ use function assert;
|
||||
* @method static DoubleTallGrass DOUBLE_TALLGRASS()
|
||||
* @method static DragonEgg DRAGON_EGG()
|
||||
* @method static DriedKelp DRIED_KELP()
|
||||
* @method static DyedShulkerBox DYED_SHULKER_BOX()
|
||||
* @method static Element ELEMENT_ACTINIUM()
|
||||
* @method static Element ELEMENT_ALUMINUM()
|
||||
* @method static Element ELEMENT_AMERICIUM()
|
||||
@ -484,6 +485,7 @@ use function assert;
|
||||
* @method static Wall SANDSTONE_WALL()
|
||||
* @method static SeaLantern SEA_LANTERN()
|
||||
* @method static SeaPickle SEA_PICKLE()
|
||||
* @method static ShulkerBox SHULKER_BOX()
|
||||
* @method static Opaque SMOOTH_QUARTZ()
|
||||
* @method static Slab SMOOTH_QUARTZ_SLAB()
|
||||
* @method static Stair SMOOTH_QUARTZ_STAIRS()
|
||||
@ -720,6 +722,7 @@ final class VanillaBlocks{
|
||||
self::register("double_tallgrass", $factory->get(175, 2));
|
||||
self::register("dragon_egg", $factory->get(122, 0));
|
||||
self::register("dried_kelp", $factory->get(394, 0));
|
||||
self::register("dyed_shulker_box", $factory->get(218, 0));
|
||||
self::register("element_actinium", $factory->get(355, 0));
|
||||
self::register("element_aluminum", $factory->get(279, 0));
|
||||
self::register("element_americium", $factory->get(361, 0));
|
||||
@ -1043,6 +1046,7 @@ final class VanillaBlocks{
|
||||
self::register("sandstone_wall", $factory->get(139, 5));
|
||||
self::register("sea_lantern", $factory->get(169, 0));
|
||||
self::register("sea_pickle", $factory->get(411, 0));
|
||||
self::register("shulker_box", $factory->get(205, 0));
|
||||
self::register("smooth_quartz", $factory->get(155, 3));
|
||||
self::register("smooth_quartz_slab", $factory->get(421, 1));
|
||||
self::register("smooth_quartz_stairs", $factory->get(440, 0));
|
||||
|
64
src/block/inventory/ShulkerBoxInventory.php
Normal file
64
src/block/inventory/ShulkerBoxInventory.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?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\block\inventory;
|
||||
|
||||
use pocketmine\block\BlockLegacyIds;
|
||||
use pocketmine\inventory\SimpleInventory;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\network\mcpe\protocol\BlockEventPacket;
|
||||
use pocketmine\world\Position;
|
||||
use pocketmine\world\sound\ShulkerCloseSound;
|
||||
use pocketmine\world\sound\ShulkerOpenSound;
|
||||
use pocketmine\world\sound\Sound;
|
||||
|
||||
class ShulkerBoxInventory extends SimpleInventory implements BlockInventory{
|
||||
use AnimatedBlockInventoryTrait;
|
||||
|
||||
public function __construct(Position $holder){
|
||||
$this->holder = $holder;
|
||||
parent::__construct(27);
|
||||
}
|
||||
|
||||
protected function getOpenSound() : Sound{
|
||||
return new ShulkerOpenSound();
|
||||
}
|
||||
|
||||
protected function getCloseSound() : Sound{
|
||||
return new ShulkerCloseSound();
|
||||
}
|
||||
|
||||
public function canAddItem(Item $item) : bool{
|
||||
if($item->getId() === BlockLegacyIds::UNDYED_SHULKER_BOX || $item->getId() === BlockLegacyIds::SHULKER_BOX){
|
||||
return false;
|
||||
}
|
||||
return parent::canAddItem($item);
|
||||
}
|
||||
|
||||
protected function animateBlock(bool $isOpen) : void{
|
||||
$holder = $this->getHolder();
|
||||
|
||||
//event ID is always 1 for a chest
|
||||
$holder->getWorld()->broadcastPacketToViewers($holder, BlockEventPacket::create(1, $isOpen ? 1 : 0, $holder->asVector3()));
|
||||
}
|
||||
}
|
120
src/block/tile/ShulkerBox.php
Normal file
120
src/block/tile/ShulkerBox.php
Normal file
@ -0,0 +1,120 @@
|
||||
<?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\block\tile;
|
||||
|
||||
use pocketmine\block\inventory\ShulkerBoxInventory;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\world\World;
|
||||
|
||||
class ShulkerBox extends Spawnable implements Container, Nameable{
|
||||
use NameableTrait {
|
||||
addAdditionalSpawnData as addNameSpawnData;
|
||||
}
|
||||
use ContainerTrait;
|
||||
|
||||
public const TAG_FACING = "facing";
|
||||
|
||||
/** @var int */
|
||||
protected $facing = Facing::NORTH;
|
||||
|
||||
/** @var ShulkerBoxInventory */
|
||||
protected $inventory;
|
||||
|
||||
public function __construct(World $world, Vector3 $pos){
|
||||
parent::__construct($world, $pos);
|
||||
$this->inventory = new ShulkerBoxInventory($this->pos);
|
||||
}
|
||||
|
||||
public function readSaveData(CompoundTag $nbt) : void{
|
||||
$this->loadName($nbt);
|
||||
$this->loadItems($nbt);
|
||||
$this->facing = $nbt->getByte(self::TAG_FACING, $this->facing);
|
||||
}
|
||||
|
||||
protected function writeSaveData(CompoundTag $nbt) : void{
|
||||
$this->saveName($nbt);
|
||||
$this->saveItems($nbt);
|
||||
$nbt->setByte(self::TAG_FACING, $this->facing);
|
||||
}
|
||||
|
||||
public function copyDataFromItem(Item $item) : void{
|
||||
$this->readSaveData($item->getNamedTag());
|
||||
if($item->hasCustomName()){
|
||||
$this->setName($item->getCustomName());
|
||||
}
|
||||
}
|
||||
|
||||
public function close() : void{
|
||||
if(!$this->closed){
|
||||
$this->inventory->removeAllViewers();
|
||||
parent::close();
|
||||
}
|
||||
}
|
||||
|
||||
protected function onBlockDestroyedHook() : void{
|
||||
//NOOP override of ContainerTrait - shulker boxes retain their contents when destroyed
|
||||
}
|
||||
|
||||
public function getCleanedNBT() : ?CompoundTag{
|
||||
$nbt = parent::getCleanedNBT();
|
||||
if($nbt !== null){
|
||||
$nbt->removeTag(self::TAG_FACING);
|
||||
}
|
||||
return $nbt;
|
||||
}
|
||||
|
||||
public function getFacing() : int{
|
||||
return $this->facing;
|
||||
}
|
||||
|
||||
public function setFacing(int $facing) : void{
|
||||
$this->facing = $facing;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ShulkerBoxInventory
|
||||
*/
|
||||
public function getInventory(){
|
||||
return $this->inventory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ShulkerBoxInventory
|
||||
*/
|
||||
public function getRealInventory(){
|
||||
return $this->inventory;
|
||||
}
|
||||
|
||||
public function getDefaultName() : string{
|
||||
return "Shulker Box";
|
||||
}
|
||||
|
||||
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
|
||||
$nbt->setByte(self::TAG_FACING, $this->facing);
|
||||
$this->addNameSpawnData($nbt);
|
||||
}
|
||||
}
|
@ -66,6 +66,7 @@ final class TileFactory{
|
||||
$this->register(Jukebox::class, ["Jukebox", "RecordPlayer", "minecraft:jukebox"]);
|
||||
$this->register(MonsterSpawner::class, ["MobSpawner", "minecraft:mob_spawner"]);
|
||||
$this->register(Note::class, ["Music", "minecraft:noteblock"]);
|
||||
$this->register(ShulkerBox::class, ["ShulkerBox", "minecraft:shulker_box"]);
|
||||
$this->register(Sign::class, ["Sign", "minecraft:sign"]);
|
||||
$this->register(Skull::class, ["Skull", "minecraft:skull"]);
|
||||
|
||||
@ -86,7 +87,6 @@ final class TileFactory{
|
||||
//TODO: MovingBlock
|
||||
//TODO: NetherReactor
|
||||
//TODO: PistonArm
|
||||
//TODO: ShulkerBox
|
||||
//TODO: Smoker
|
||||
//TODO: StructureBlock
|
||||
}
|
||||
|
34
src/world/sound/ShulkerCloseSound.php
Normal file
34
src/world/sound/ShulkerCloseSound.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?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\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
|
||||
class ShulkerCloseSound implements Sound{
|
||||
|
||||
public function encode(?Vector3 $pos) : array{
|
||||
return [LevelSoundEventPacket::create(LevelSoundEventPacket::SOUND_SHULKERBOX_CLOSED, $pos)];
|
||||
}
|
||||
}
|
34
src/world/sound/ShulkerOpenSound.php
Normal file
34
src/world/sound/ShulkerOpenSound.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?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\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
|
||||
class ShulkerOpenSound implements Sound{
|
||||
|
||||
public function encode(?Vector3 $pos) : array{
|
||||
return [LevelSoundEventPacket::create(LevelSoundEventPacket::SOUND_SHULKERBOX_OPEN, $pos)];
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user