From ff2a3baa8e5812562728cbeb1c5ac317e71d7d74 Mon Sep 17 00:00:00 2001 From: Jack Honour Date: Fri, 7 Aug 2020 21:07:58 +0100 Subject: [PATCH] Implemented Jukebox & Records (#3742) Co-authored-by: Dylan K. Taylor --- src/block/BlockFactory.php | 2 + src/block/Jukebox.php | 121 ++++++++++++++++++ src/block/VanillaBlocks.php | 2 + src/block/tile/Jukebox.php | 65 ++++++++++ src/block/tile/TileFactory.php | 1 + src/block/utils/RecordType.php | 95 ++++++++++++++ src/item/ItemFactory.php | 26 ++-- src/item/Record.php | 44 +++++++ src/item/VanillaItems.php | 24 ++++ src/network/mcpe/NetworkSession.php | 7 + src/player/Player.php | 9 ++ src/world/sound/RecordSound.php | 42 ++++++ src/world/sound/RecordStopSound.php | 34 +++++ .../block_factory_consistency_check.json | 2 +- 14 files changed, 461 insertions(+), 13 deletions(-) create mode 100644 src/block/Jukebox.php create mode 100644 src/block/tile/Jukebox.php create mode 100644 src/block/utils/RecordType.php create mode 100644 src/item/Record.php create mode 100644 src/world/sound/RecordSound.php create mode 100644 src/world/sound/RecordStopSound.php diff --git a/src/block/BlockFactory.php b/src/block/BlockFactory.php index c9c2fe61d..d7ec1f88b 100644 --- a/src/block/BlockFactory.php +++ b/src/block/BlockFactory.php @@ -39,6 +39,7 @@ use pocketmine\block\tile\FlowerPot as TileFlowerPot; use pocketmine\block\tile\Furnace as TileFurnace; use pocketmine\block\tile\Hopper as TileHopper; 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\Skull as TileSkull; @@ -230,6 +231,7 @@ class BlockFactory{ $this->register(new Opaque(new BID(Ids::IRON_ORE), "Iron Ore", new BlockBreakInfo(3.0, BlockToolType::PICKAXE, ToolTier::STONE()->getHarvestLevel()))); $this->register(new Trapdoor(new BID(Ids::IRON_TRAPDOOR), "Iron Trapdoor", new BlockBreakInfo(5.0, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 25.0))); $this->register(new ItemFrame(new BID(Ids::FRAME_BLOCK, 0, ItemIds::FRAME, TileItemFrame::class), "Item Frame")); + $this->register(new Jukebox(new BID(Ids::JUKEBOX, 0, ItemIds::JUKEBOX, TileJukebox::class), "Jukebox")); $this->register(new Ladder(new BID(Ids::LADDER), "Ladder")); $this->register(new Lantern(new BID(Ids::LANTERN), "Lantern", new BlockBreakInfo(5.0, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel()))); $this->register(new Opaque(new BID(Ids::LAPIS_BLOCK), "Lapis Lazuli Block", new BlockBreakInfo(3.0, BlockToolType::PICKAXE, ToolTier::STONE()->getHarvestLevel()))); diff --git a/src/block/Jukebox.php b/src/block/Jukebox.php new file mode 100644 index 000000000..608e9194c --- /dev/null +++ b/src/block/Jukebox.php @@ -0,0 +1,121 @@ +record !== null){ + $this->ejectRecord(); + }elseif($item instanceof Record){ + $player->sendJukeboxPopup("record.nowPlaying", ["%" . $item->getRecordType()->getTranslationKey()]); + $this->insertRecord($item->pop()); + } + } + + $this->pos->getWorld()->setBlock($this->pos, $this); + + return true; + } + + public function getRecord() : ?Record{ + return $this->record; + } + + public function ejectRecord() : void{ + if($this->record !== null){ + $this->getPos()->getWorld()->dropItem($this->getPos()->add(0.5, 1, 0.5), $this->record); + $this->record = null; + $this->stopSound(); + } + } + + public function insertRecord(Record $record) : void{ + if($this->record === null){ + $this->record = $record; + $this->startSound(); + } + } + + public function startSound() : void{ + if($this->record !== null){ + $this->getPos()->getWorld()->addSound($this->getPos(), new RecordSound($this->record->getRecordType())); + } + } + + public function stopSound() : void{ + $this->getPos()->getWorld()->addSound($this->getPos(), new RecordStopSound()); + } + + public function onBreak(Item $item, ?Player $player = null) : bool{ + $this->stopSound(); + return parent::onBreak($item, $player); + } + + public function getDropsForCompatibleTool(Item $item) : array{ + $drops = parent::getDropsForCompatibleTool($item); + if($this->record !== null){ + $drops[] = $this->record; + } + return $drops; + } + + public function readStateFromWorld() : void{ + parent::readStateFromWorld(); + $jukebox = $this->pos->getWorld()->getTile($this->pos); + if($jukebox instanceof JukeboxTile){ + $this->record = $jukebox->getRecord(); + } + } + + public function writeStateToWorld() : void{ + parent::writeStateToWorld(); + $jukebox = $this->pos->getWorld()->getTile($this->pos); + if($jukebox instanceof JukeboxTile){ + $jukebox->setRecord($this->record); + } + } + + //TODO: Jukebox has redstone effects, they are not implemented. +} diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index 487d70f0e..6f7fb15cd 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -397,6 +397,7 @@ use function assert; * @method static Opaque IRON_ORE() * @method static Trapdoor IRON_TRAPDOOR() * @method static ItemFrame ITEM_FRAME() + * @method static Jukebox JUKEBOX() * @method static WoodenButton JUNGLE_BUTTON() * @method static WoodenDoor JUNGLE_DOOR() * @method static WoodenFence JUNGLE_FENCE() @@ -1062,6 +1063,7 @@ final class VanillaBlocks{ self::register("iron_ore", $factory->get(15)); self::register("iron_trapdoor", $factory->get(167)); self::register("item_frame", $factory->get(199)); + self::register("jukebox", $factory->get(84)); self::register("jungle_button", $factory->get(398)); self::register("jungle_door", $factory->get(195)); self::register("jungle_fence", $factory->get(85, 3)); diff --git a/src/block/tile/Jukebox.php b/src/block/tile/Jukebox.php new file mode 100644 index 000000000..35242ed8d --- /dev/null +++ b/src/block/tile/Jukebox.php @@ -0,0 +1,65 @@ +record; + } + + public function setRecord(?Record $record) : void{ + $this->record = $record; + } + + public function readSaveData(CompoundTag $nbt) : void{ + if(($tag = $nbt->getCompoundTag(self::TAG_RECORD)) !== null){ + $record = Item::nbtDeserialize($tag); + if($record instanceof Record){ + $this->record = $record; + } + } + } + + protected function writeSaveData(CompoundTag $nbt) : void{ + if($this->record !== null){ + $nbt->setTag(self::TAG_RECORD, $this->record->nbtSerialize()); + } + } + + protected function addAdditionalSpawnData(CompoundTag $nbt) : void{ + //this is needed for the note particles to show on the client side + if($this->record !== null){ + $nbt->setTag(self::TAG_RECORD, $this->record->nbtSerialize()); + } + } +} diff --git a/src/block/tile/TileFactory.php b/src/block/tile/TileFactory.php index 3036d3079..011f71c88 100644 --- a/src/block/tile/TileFactory.php +++ b/src/block/tile/TileFactory.php @@ -60,6 +60,7 @@ final class TileFactory{ $this->register(Furnace::class, ["Furnace", "minecraft:furnace"]); $this->register(Hopper::class, ["Hopper", "minecraft:hopper"]); $this->register(ItemFrame::class, ["ItemFrame"]); //this is an entity in PC + $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(Sign::class, ["Sign", "minecraft:sign"]); diff --git a/src/block/utils/RecordType.php b/src/block/utils/RecordType.php new file mode 100644 index 000000000..9846e3716 --- /dev/null +++ b/src/block/utils/RecordType.php @@ -0,0 +1,95 @@ +Enum___construct($enumName); + $this->soundName = $soundName; + $this->soundId = $soundId; + $this->translationKey = $translationKey; + } + + public function getSoundName() : string{ + return $this->soundName; + } + + public function getSoundId() : int{ + return $this->soundId; + } + + public function getTranslationKey() : string{ + return $this->translationKey; + } +} diff --git a/src/item/ItemFactory.php b/src/item/ItemFactory.php index 479fe661c..7c3ec826c 100644 --- a/src/item/ItemFactory.php +++ b/src/item/ItemFactory.php @@ -26,6 +26,7 @@ namespace pocketmine\item; use pocketmine\block\BlockFactory; use pocketmine\block\BlockLegacyIds; use pocketmine\block\utils\DyeColor; +use pocketmine\block\utils\RecordType; use pocketmine\block\utils\SkullType; use pocketmine\block\utils\TreeType; use pocketmine\block\VanillaBlocks; @@ -215,6 +216,18 @@ class ItemFactory{ $this->register(new RawPorkchop(new ItemIdentifier(ItemIds::RAW_PORKCHOP, 0), "Raw Porkchop")); $this->register(new RawRabbit(new ItemIdentifier(ItemIds::RAW_RABBIT, 0), "Raw Rabbit")); $this->register(new RawSalmon(new ItemIdentifier(ItemIds::RAW_SALMON, 0), "Raw Salmon")); + $this->register(new Record(new ItemIdentifier(ItemIds::RECORD_13, 0), RecordType::DISK_13(), "Record 13")); + $this->register(new Record(new ItemIdentifier(ItemIds::RECORD_CAT, 0), RecordType::DISK_CAT(), "Record Cat")); + $this->register(new Record(new ItemIdentifier(ItemIds::RECORD_BLOCKS, 0), RecordType::DISK_BLOCKS(), "Record Blocks")); + $this->register(new Record(new ItemIdentifier(ItemIds::RECORD_CHIRP, 0), RecordType::DISK_CHIRP(), "Record Chirp")); + $this->register(new Record(new ItemIdentifier(ItemIds::RECORD_FAR, 0), RecordType::DISK_FAR(), "Record Far")); + $this->register(new Record(new ItemIdentifier(ItemIds::RECORD_MALL, 0), RecordType::DISK_MALL(), "Record Mall")); + $this->register(new Record(new ItemIdentifier(ItemIds::RECORD_MELLOHI, 0), RecordType::DISK_MELLOHI(), "Record Mellohi")); + $this->register(new Record(new ItemIdentifier(ItemIds::RECORD_STAL, 0), RecordType::DISK_STAL(), "Record Stal")); + $this->register(new Record(new ItemIdentifier(ItemIds::RECORD_STRAD, 0), RecordType::DISK_STRAD(), "Record Strad")); + $this->register(new Record(new ItemIdentifier(ItemIds::RECORD_WARD, 0), RecordType::DISK_WARD(), "Record Ward")); + $this->register(new Record(new ItemIdentifier(ItemIds::RECORD_11, 0), RecordType::DISK_11(), "Record 11")); + $this->register(new Record(new ItemIdentifier(ItemIds::RECORD_WAIT, 0), RecordType::DISK_WAIT(), "Record Wait")); $this->register(new Redstone(new ItemIdentifier(ItemIds::REDSTONE, 0), "Redstone")); $this->register(new RottenFlesh(new ItemIdentifier(ItemIds::ROTTEN_FLESH, 0), "Rotten Flesh")); $this->register(new Shears(new ItemIdentifier(ItemIds::SHEARS, 0), "Shears")); @@ -294,18 +307,7 @@ class ItemFactory{ //TODO: minecraft:name_tag //TODO: minecraft:phantom_membrane //TODO: minecraft:rapid_fertilizer - //TODO: minecraft:record_11 - //TODO: minecraft:record_13 - //TODO: minecraft:record_blocks - //TODO: minecraft:record_cat - //TODO: minecraft:record_chirp - //TODO: minecraft:record_far - //TODO: minecraft:record_mall - //TODO: minecraft:record_mellohi - //TODO: minecraft:record_stal - //TODO: minecraft:record_strad - //TODO: minecraft:record_wait - //TODO: minecraft:record_ward + //TODO: minecraft:record_pigstep //TODO: minecraft:saddle //TODO: minecraft:shield //TODO: minecraft:sparkler diff --git a/src/item/Record.php b/src/item/Record.php new file mode 100644 index 000000000..35c56bb67 --- /dev/null +++ b/src/item/Record.php @@ -0,0 +1,44 @@ +recordType = $recordType; + parent::__construct($identifier, $name); + } + + public function getRecordType() : RecordType{ + return $this->recordType; + } + + public function getMaxStackSize() : int{ + return 1; + } +} diff --git a/src/item/VanillaItems.php b/src/item/VanillaItems.php index e44aa31b7..ba5cd091c 100644 --- a/src/item/VanillaItems.php +++ b/src/item/VanillaItems.php @@ -247,6 +247,18 @@ use function assert; * @method static RawPorkchop RAW_PORKCHOP() * @method static RawRabbit RAW_RABBIT() * @method static RawSalmon RAW_SALMON() + * @method static Record RECORD_11() + * @method static Record RECORD_13() + * @method static Record RECORD_BLOCKS() + * @method static Record RECORD_CAT() + * @method static Record RECORD_CHIRP() + * @method static Record RECORD_FAR() + * @method static Record RECORD_MALL() + * @method static Record RECORD_MELLOHI() + * @method static Record RECORD_STAL() + * @method static Record RECORD_STRAD() + * @method static Record RECORD_WAIT() + * @method static Record RECORD_WARD() * @method static Banner RED_BANNER() * @method static Bed RED_BED() * @method static Dye RED_DYE() @@ -535,6 +547,18 @@ final class VanillaItems{ self::register("raw_porkchop", $factory->get(319)); self::register("raw_rabbit", $factory->get(411)); self::register("raw_salmon", $factory->get(460)); + self::register("record_11", $factory->get(510)); + self::register("record_13", $factory->get(500)); + self::register("record_blocks", $factory->get(502)); + self::register("record_cat", $factory->get(501)); + self::register("record_chirp", $factory->get(503)); + self::register("record_far", $factory->get(504)); + self::register("record_mall", $factory->get(505)); + self::register("record_mellohi", $factory->get(506)); + self::register("record_stal", $factory->get(507)); + self::register("record_strad", $factory->get(508)); + self::register("record_wait", $factory->get(511)); + self::register("record_ward", $factory->get(509)); self::register("red_banner", $factory->get(446, 1)); self::register("red_bed", $factory->get(355, 14)); self::register("red_dye", $factory->get(351, 1)); diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 71832e7fc..a8f5e8502 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -792,6 +792,13 @@ class NetworkSession{ $this->sendDataPacket(TextPacket::translation($key, $parameters)); } + /** + * @param string[] $parameters + */ + public function onJukeboxPopup(string $key, array $parameters) : void{ + $this->sendDataPacket(TextPacket::jukeboxPopup($key, $parameters)); + } + public function onPopup(string $message) : void{ $this->sendDataPacket(TextPacket::popup($message)); } diff --git a/src/player/Player.php b/src/player/Player.php index a4fb358b8..c146068ca 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -1851,6 +1851,15 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ } } + /** + * @param string[] $args + */ + public function sendJukeboxPopup(string $key, array $args) : void{ + if($this->networkSession !== null){ + $this->networkSession->onJukeboxPopup($key, $args); + } + } + /** * Sends a popup message to the player * diff --git a/src/world/sound/RecordSound.php b/src/world/sound/RecordSound.php new file mode 100644 index 000000000..1ec5c23ca --- /dev/null +++ b/src/world/sound/RecordSound.php @@ -0,0 +1,42 @@ +recordType = $recordType; + } + + public function encode(?Vector3 $pos){ + return LevelSoundEventPacket::create($this->recordType->getSoundId(), $pos); + } +} diff --git a/src/world/sound/RecordStopSound.php b/src/world/sound/RecordStopSound.php new file mode 100644 index 000000000..b5f87f87f --- /dev/null +++ b/src/world/sound/RecordStopSound.php @@ -0,0 +1,34 @@ +