From 0629d11e130ba3d9b2b240c97dfbecfa79e121a1 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 16 Apr 2023 20:51:55 +0100 Subject: [PATCH 01/46] Avoid unnecessary events work in handleDataPacket if the events have no registered handlers this particular optimisation became possible thanks to changes in 4.19. I observed that the allocation of Event objects and calling ->call() was costing us a significant percentage of the time taken in PlayerAuthInputPacket handlers. This change produces a measurable 2 microsecond reduction in overhead for PlayerAuthInputPacket handling when players are not moving (10.7 -> 8.7 microseconds). On a server with 200 players, this translates into a 1% reduction in CPU load for PlayerAuthInputPacket alone. It will also benefit other packets, but not to the extent that PlayerAuthInputPacket benefits. --- src/event/Event.php | 11 +++++++++ src/network/mcpe/NetworkSession.php | 35 ++++++++++++++++------------- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/event/Event.php b/src/event/Event.php index 1ae7bb96f..6264d8525 100644 --- a/src/event/Event.php +++ b/src/event/Event.php @@ -27,6 +27,7 @@ declare(strict_types=1); namespace pocketmine\event; use pocketmine\timings\Timings; +use function count; use function get_class; abstract class Event{ @@ -67,4 +68,14 @@ abstract class Event{ $timings->stopTiming(); } } + + /** + * Returns whether the current class context has any registered global handlers. + * This can be used in hot code paths to avoid unnecessary event object creation. + * + * Usage: SomeEventClass::hasHandlers() + */ + public static function hasHandlers() : bool{ + return count(HandlerListManager::global()->getListFor(static::class)->getListenerList()) > 0; + } } diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 7f07fbd77..dfba61bab 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -401,10 +401,12 @@ class NetworkSession{ $timings->startTiming(); try{ - $ev = new DataPacketDecodeEvent($this, $packet->pid(), $buffer); - $ev->call(); - if($ev->isCancelled()){ - return; + if(DataPacketDecodeEvent::hasHandlers()){ + $ev = new DataPacketDecodeEvent($this, $packet->pid(), $buffer); + $ev->call(); + if($ev->isCancelled()){ + return; + } } $decodeTimings = Timings::getDecodeDataPacketTimings($packet); @@ -424,19 +426,22 @@ class NetworkSession{ $decodeTimings->stopTiming(); } - $ev = new DataPacketReceiveEvent($this, $packet); - $ev->call(); - if(!$ev->isCancelled()){ - $handlerTimings = Timings::getHandleDataPacketTimings($packet); - $handlerTimings->startTiming(); - try{ - if($this->handler === null || !$packet->handle($this->handler)){ - $this->logger->debug("Unhandled " . $packet->getName() . ": " . base64_encode($stream->getBuffer())); - } - }finally{ - $handlerTimings->stopTiming(); + if(DataPacketReceiveEvent::hasHandlers()){ + $ev = new DataPacketReceiveEvent($this, $packet); + $ev->call(); + if($ev->isCancelled()){ + return; } } + $handlerTimings = Timings::getHandleDataPacketTimings($packet); + $handlerTimings->startTiming(); + try{ + if($this->handler === null || !$packet->handle($this->handler)){ + $this->logger->debug("Unhandled " . $packet->getName() . ": " . base64_encode($stream->getBuffer())); + } + }finally{ + $handlerTimings->stopTiming(); + } }finally{ $timings->stopTiming(); } From 26086372107283aad399fb0613d1c284561398be Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 1 Aug 2023 17:37:49 +0100 Subject: [PATCH 02/46] HandlerListManager: track RegisteredListenerCache directly This change improves the performance of calling an event with 0 handlers by about 10% with no other changes. Since we have to access the list eventually anyway, we can cut out some unnecessary work by returning the handlers from the cache directly, instead of fetching the HandlerList for no reason. This also improves the performance of Event::hasHandlers() by about 40%, which is pretty significant (120 ns -> 80 ns). --- src/event/Event.php | 6 +++--- src/event/HandlerList.php | 6 ++---- src/event/HandlerListManager.php | 25 ++++++++++++++++++++++++- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/event/Event.php b/src/event/Event.php index 49652fcc6..21b8ae36a 100644 --- a/src/event/Event.php +++ b/src/event/Event.php @@ -55,11 +55,11 @@ abstract class Event{ $timings = Timings::getEventTimings($this); $timings->startTiming(); - $handlerList = HandlerListManager::global()->getListFor(get_class($this)); + $handlers = HandlerListManager::global()->getHandlersFor(static::class); ++self::$eventCallDepth; try{ - foreach($handlerList->getListenerList() as $registration){ + foreach($handlers as $registration){ $registration->callEvent($this); } }finally{ @@ -75,6 +75,6 @@ abstract class Event{ * Usage: SomeEventClass::hasHandlers() */ public static function hasHandlers() : bool{ - return count(HandlerListManager::global()->getListFor(static::class)->getListenerList()) > 0; + return count(HandlerListManager::global()->getHandlersFor(static::class)) > 0; } } diff --git a/src/event/HandlerList.php b/src/event/HandlerList.php index 37811e959..74eedf3a4 100644 --- a/src/event/HandlerList.php +++ b/src/event/HandlerList.php @@ -33,8 +33,6 @@ class HandlerList{ /** @var RegisteredListener[][] */ private array $handlerSlots = []; - private RegisteredListenerCache $handlerCache; - /** @var RegisteredListenerCache[] */ private array $affectedHandlerCaches = []; @@ -44,9 +42,9 @@ class HandlerList{ */ public function __construct( private string $class, - private ?HandlerList $parentList + private ?HandlerList $parentList, + private RegisteredListenerCache $handlerCache = new RegisteredListenerCache() ){ - $this->handlerCache = new RegisteredListenerCache(); for($list = $this; $list !== null; $list = $list->parentList){ $list->affectedHandlerCaches[spl_object_id($this->handlerCache)] = $this->handlerCache; } diff --git a/src/event/HandlerListManager.php b/src/event/HandlerListManager.php index ab94674cf..047632f54 100644 --- a/src/event/HandlerListManager.php +++ b/src/event/HandlerListManager.php @@ -36,6 +36,11 @@ class HandlerListManager{ /** @var HandlerList[] classname => HandlerList */ private array $allLists = []; + /** + * @var RegisteredListenerCache[] event class name => cache + * @phpstan-var array, RegisteredListenerCache> + */ + private array $handlerCaches = []; /** * Unregisters all the listeners @@ -98,7 +103,25 @@ class HandlerListManager{ } $parent = self::resolveNearestHandleableParent($class); - return $this->allLists[$event] = new HandlerList($event, $parent !== null ? $this->getListFor($parent->getName()) : null); + $cache = new RegisteredListenerCache(); + $this->handlerCaches[$event] = $cache; + return $this->allLists[$event] = new HandlerList( + $event, + parentList: $parent !== null ? $this->getListFor($parent->getName()) : null, + handlerCache: $cache + ); + } + + /** + * @phpstan-template TEvent of Event + * @phpstan-param class-string $event + * + * @return RegisteredListener[] + */ + public function getHandlersFor(string $event) : array{ + $cache = $this->handlerCaches[$event] ?? null; + //getListFor() will populate the cache for the next call + return $cache?->list ?? $this->getListFor($event)->getListenerList(); } /** From 0b86fafafbb8630543ff8d6c35645e42f76721e9 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 1 Aug 2023 17:41:53 +0100 Subject: [PATCH 03/46] Hot path optimisation for DataPacketSendEvent --- src/network/mcpe/NetworkSession.php | 14 +++++++++----- src/network/mcpe/StandardPacketBroadcaster.php | 12 +++++++----- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 894b1434b..100d9c989 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -464,12 +464,16 @@ class NetworkSession{ $timings = Timings::getSendDataPacketTimings($packet); $timings->startTiming(); try{ - $ev = new DataPacketSendEvent([$this], [$packet]); - $ev->call(); - if($ev->isCancelled()){ - return false; + if(DataPacketSendEvent::hasHandlers()){ + $ev = new DataPacketSendEvent([$this], [$packet]); + $ev->call(); + if($ev->isCancelled()){ + return false; + } + $packets = $ev->getPackets(); + }else{ + $packets = [$packet]; } - $packets = $ev->getPackets(); foreach($packets as $evPacket){ $this->addToSendBuffer(self::encodePacketTimed(PacketSerializer::encoder($this->packetSerializerContext), $evPacket)); diff --git a/src/network/mcpe/StandardPacketBroadcaster.php b/src/network/mcpe/StandardPacketBroadcaster.php index 9de3d214a..c200859fd 100644 --- a/src/network/mcpe/StandardPacketBroadcaster.php +++ b/src/network/mcpe/StandardPacketBroadcaster.php @@ -44,12 +44,14 @@ final class StandardPacketBroadcaster implements PacketBroadcaster{ public function broadcastPackets(array $recipients, array $packets) : void{ //TODO: this shouldn't really be called here, since the broadcaster might be replaced by an alternative //implementation that doesn't fire events - $ev = new DataPacketSendEvent($recipients, $packets); - $ev->call(); - if($ev->isCancelled()){ - return; + if(DataPacketSendEvent::hasHandlers()){ + $ev = new DataPacketSendEvent($recipients, $packets); + $ev->call(); + if($ev->isCancelled()){ + return; + } + $packets = $ev->getPackets(); } - $packets = $ev->getPackets(); $compressors = []; From 82f87cc2dac70b0dd74a26534f7e04fef88ce3a3 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 2 Aug 2023 13:40:12 +0100 Subject: [PATCH 04/46] Reduce repeated block-change-event related code the new helper code reveals even more repetition, but this is at least consistent now. --- src/block/BaseCoral.php | 8 +- src/block/Cactus.php | 9 +-- src/block/CaveVines.php | 26 ++---- src/block/CocoaBlock.php | 9 +-- src/block/ConcretePowder.php | 8 +- src/block/CoralBlock.php | 8 +- src/block/Crops.php | 14 +--- src/block/Fire.php | 11 +-- src/block/FrostedIce.php | 8 +- src/block/GlowLichen.php | 11 +-- src/block/Grass.php | 14 +--- src/block/Ice.php | 8 +- src/block/Liquid.php | 10 +-- src/block/Mycelium.php | 8 +- src/block/NetherWartPlant.php | 8 +- src/block/SnowLayer.php | 8 +- src/block/Stem.php | 15 +--- src/block/Sugarcane.php | 10 +-- src/block/SweetBerryBush.php | 16 +--- src/block/utils/BlockEventHelper.php | 115 +++++++++++++++++++++++++++ 20 files changed, 166 insertions(+), 158 deletions(-) create mode 100644 src/block/utils/BlockEventHelper.php diff --git a/src/block/BaseCoral.php b/src/block/BaseCoral.php index 73b2c48d6..3a6acd5d1 100644 --- a/src/block/BaseCoral.php +++ b/src/block/BaseCoral.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\CoralType; use pocketmine\block\utils\CoralTypeTrait; use pocketmine\block\utils\SupportType; -use pocketmine\event\block\BlockDeathEvent; use pocketmine\item\Item; use function mt_rand; @@ -46,11 +46,7 @@ abstract class BaseCoral extends Transparent{ public function onScheduledUpdate() : void{ if(!$this->dead && !$this->isCoveredWithWater()){ - $ev = new BlockDeathEvent($this, $this->setDead(true)); - $ev->call(); - if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::die($this, (clone $this)->setDead(true)); } } diff --git a/src/block/Cactus.php b/src/block/Cactus.php index 8fff294f6..0176d17bd 100644 --- a/src/block/Cactus.php +++ b/src/block/Cactus.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\entity\Entity; -use pocketmine\event\block\BlockGrowEvent; use pocketmine\event\entity\EntityDamageByBlockEvent; use pocketmine\event\entity\EntityDamageEvent; use pocketmine\item\Item; @@ -111,12 +111,7 @@ class Cactus extends Transparent{ } $b = $world->getBlockAt($this->position->x, $this->position->y + $y, $this->position->z); if($b->getTypeId() === BlockTypeIds::AIR){ - $ev = new BlockGrowEvent($b, VanillaBlocks::CACTUS()); - $ev->call(); - if($ev->isCancelled()){ - break; - } - $world->setBlock($b->position, $ev->getNewState()); + BlockEventHelper::grow($b, VanillaBlocks::CACTUS(), null); }else{ break; } diff --git a/src/block/CaveVines.php b/src/block/CaveVines.php index e56b8b720..6ff934881 100644 --- a/src/block/CaveVines.php +++ b/src/block/CaveVines.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\entity\Entity; -use pocketmine\event\block\BlockGrowEvent; use pocketmine\item\Fertilizer; use pocketmine\item\Item; use pocketmine\item\VanillaItems; @@ -114,16 +114,12 @@ class CaveVines extends Flowable{ return true; } if($item instanceof Fertilizer){ - $ev = new BlockGrowEvent($this, (clone $this) + $newState = (clone $this) ->setBerries(true) - ->setHead(!$this->getSide(Facing::DOWN)->hasSameTypeId($this)) - ); - $ev->call(); - if($ev->isCancelled()){ - return false; + ->setHead(!$this->getSide(Facing::DOWN)->hasSameTypeId($this)); + if(BlockEventHelper::grow($this, $newState, $player)){ + $item->pop(); } - $item->pop(); - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); return true; } return false; @@ -141,16 +137,10 @@ class CaveVines extends Flowable{ if($world->isInWorld($growthPos->getFloorX(), $growthPos->getFloorY(), $growthPos->getFloorZ())){ $block = $world->getBlock($growthPos); if($block->getTypeId() === BlockTypeIds::AIR){ - $ev = new BlockGrowEvent($block, VanillaBlocks::CAVE_VINES() + $newState = VanillaBlocks::CAVE_VINES() ->setAge($this->age + 1) - ->setBerries(mt_rand(1, 9) === 1) - ); - - $ev->call(); - - if(!$ev->isCancelled()){ - $world->setBlock($growthPos, $ev->getNewState()); - } + ->setBerries(mt_rand(1, 9) === 1); + BlockEventHelper::grow($block, $newState, null); } } } diff --git a/src/block/CocoaBlock.php b/src/block/CocoaBlock.php index aafce3169..69f94eaf6 100644 --- a/src/block/CocoaBlock.php +++ b/src/block/CocoaBlock.php @@ -23,11 +23,11 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\block\utils\WoodType; use pocketmine\data\runtime\RuntimeDataDescriber; -use pocketmine\event\block\BlockGrowEvent; use pocketmine\item\Fertilizer; use pocketmine\item\Item; use pocketmine\item\VanillaItems; @@ -123,12 +123,7 @@ class CocoaBlock extends Transparent{ if($this->age < self::MAX_AGE){ $block = clone $this; $block->age++; - $ev = new BlockGrowEvent($this, $block, $player); - $ev->call(); - if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); - return true; - } + return BlockEventHelper::grow($this, $block, $player); } return false; } diff --git a/src/block/ConcretePowder.php b/src/block/ConcretePowder.php index 02fa31adb..635d3ff2c 100644 --- a/src/block/ConcretePowder.php +++ b/src/block/ConcretePowder.php @@ -23,11 +23,11 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\ColoredTrait; use pocketmine\block\utils\DyeColor; use pocketmine\block\utils\Fallable; use pocketmine\block\utils\FallableTrait; -use pocketmine\event\block\BlockFormEvent; use pocketmine\math\Facing; class ConcretePowder extends Opaque implements Fallable{ @@ -43,11 +43,7 @@ class ConcretePowder extends Opaque implements Fallable{ public function onNearbyBlockChange() : void{ if(($water = $this->getAdjacentWater()) !== null){ - $ev = new BlockFormEvent($this, VanillaBlocks::CONCRETE()->setColor($this->color), $water); - $ev->call(); - if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::form($this, VanillaBlocks::CONCRETE()->setColor($this->color), $water); }else{ $this->startFalling(); } diff --git a/src/block/CoralBlock.php b/src/block/CoralBlock.php index b90246561..3aaf5b752 100644 --- a/src/block/CoralBlock.php +++ b/src/block/CoralBlock.php @@ -23,9 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\CoralType; use pocketmine\block\utils\CoralTypeTrait; -use pocketmine\event\block\BlockDeathEvent; use pocketmine\item\Item; use function mt_rand; @@ -55,11 +55,7 @@ final class CoralBlock extends Opaque{ } } if(!$hasWater){ - $ev = new BlockDeathEvent($this, $this->setDead(true)); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::die($this, (clone $this)->setDead(true)); } } } diff --git a/src/block/Crops.php b/src/block/Crops.php index 8949e663b..d6e84c424 100644 --- a/src/block/Crops.php +++ b/src/block/Crops.php @@ -23,8 +23,8 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\data\runtime\RuntimeDataDescriber; -use pocketmine\event\block\BlockGrowEvent; use pocketmine\item\Fertilizer; use pocketmine\item\Item; use pocketmine\math\Facing; @@ -68,11 +68,7 @@ abstract class Crops extends Flowable{ if($block->age > self::MAX_AGE){ $block->age = self::MAX_AGE; } - - $ev = new BlockGrowEvent($this, $block, $player); - $ev->call(); - if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); + if(BlockEventHelper::grow($this, $block, $player)){ $item->pop(); } @@ -96,11 +92,7 @@ abstract class Crops extends Flowable{ if($this->age < self::MAX_AGE && mt_rand(0, 2) === 1){ $block = clone $this; ++$block->age; - $ev = new BlockGrowEvent($this, $block); - $ev->call(); - if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::grow($this, $block, null); } } } diff --git a/src/block/Fire.php b/src/block/Fire.php index 0a3b2e3e3..a39ef6b9c 100644 --- a/src/block/Fire.php +++ b/src/block/Fire.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\event\block\BlockBurnEvent; -use pocketmine\event\block\BlockSpreadEvent; use pocketmine\math\Facing; use pocketmine\world\format\Chunk; use pocketmine\world\World; @@ -225,13 +225,6 @@ class Fire extends BaseFire{ } private function spreadBlock(Block $block, Block $newState) : bool{ - $ev = new BlockSpreadEvent($block, $this, $newState); - $ev->call(); - if(!$ev->isCancelled()){ - $block->position->getWorld()->setBlock($block->position, $ev->getNewState()); - return true; - } - - return false; + return BlockEventHelper::spread($block, $newState, $this); } } diff --git a/src/block/FrostedIce.php b/src/block/FrostedIce.php index 38c552a95..039fe49f3 100644 --- a/src/block/FrostedIce.php +++ b/src/block/FrostedIce.php @@ -23,8 +23,8 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\data\runtime\RuntimeDataDescriber; -use pocketmine\event\block\BlockMeltEvent; use function mt_rand; class FrostedIce extends Ice{ @@ -97,11 +97,7 @@ class FrostedIce extends Ice{ private function tryMelt() : bool{ $world = $this->position->getWorld(); if($this->age >= self::MAX_AGE){ - $ev = new BlockMeltEvent($this, VanillaBlocks::WATER()); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::melt($this, VanillaBlocks::WATER()); return true; } diff --git a/src/block/GlowLichen.php b/src/block/GlowLichen.php index 39ce512a6..84dec29ce 100644 --- a/src/block/GlowLichen.php +++ b/src/block/GlowLichen.php @@ -23,9 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; -use pocketmine\event\block\BlockSpreadEvent; use pocketmine\item\Fertilizer; use pocketmine\item\Item; use pocketmine\math\AxisAlignedBB; @@ -166,14 +166,7 @@ class GlowLichen extends Transparent{ return false; } - $ev = new BlockSpreadEvent($replacedBlock, $this, $replacementBlock); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($replacedBlock->getPosition(), $ev->getNewState()); - return true; - } - - return false; + return BlockEventHelper::spread($replacedBlock, $replacementBlock, $this); } /** diff --git a/src/block/Grass.php b/src/block/Grass.php index 54975d046..709dc6a9d 100644 --- a/src/block/Grass.php +++ b/src/block/Grass.php @@ -23,8 +23,8 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\DirtType; -use pocketmine\event\block\BlockSpreadEvent; use pocketmine\item\Fertilizer; use pocketmine\item\Hoe; use pocketmine\item\Item; @@ -58,11 +58,7 @@ class Grass extends Opaque{ $lightAbove = $world->getFullLightAt($this->position->x, $this->position->y + 1, $this->position->z); if($lightAbove < 4 && $world->getBlockAt($this->position->x, $this->position->y + 1, $this->position->z)->getLightFilter() >= 2){ //grass dies - $ev = new BlockSpreadEvent($this, $this, VanillaBlocks::DIRT()); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($this->position, $ev->getNewState(), false); - } + BlockEventHelper::spread($this, VanillaBlocks::DIRT(), $this); }elseif($lightAbove >= 9){ //try grass spread for($i = 0; $i < 4; ++$i){ @@ -80,11 +76,7 @@ class Grass extends Opaque{ continue; } - $ev = new BlockSpreadEvent($b, $this, VanillaBlocks::GRASS()); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($b->position, $ev->getNewState(), false); - } + BlockEventHelper::spread($b, VanillaBlocks::GRASS(), $this); } } } diff --git a/src/block/Ice.php b/src/block/Ice.php index 41f60e3f9..ca0302f90 100644 --- a/src/block/Ice.php +++ b/src/block/Ice.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace pocketmine\block; -use pocketmine\event\block\BlockMeltEvent; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\item\enchantment\VanillaEnchantments; use pocketmine\item\Item; use pocketmine\player\Player; @@ -53,11 +53,7 @@ class Ice extends Transparent{ public function onRandomTick() : void{ $world = $this->position->getWorld(); if($world->getHighestAdjacentBlockLight($this->position->x, $this->position->y, $this->position->z) >= 12){ - $ev = new BlockMeltEvent($this, VanillaBlocks::WATER()); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::melt($this, VanillaBlocks::WATER()); } } diff --git a/src/block/Liquid.php b/src/block/Liquid.php index a56f666a2..e102540ec 100644 --- a/src/block/Liquid.php +++ b/src/block/Liquid.php @@ -23,11 +23,11 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\MinimumCostFlowCalculator; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\entity\Entity; -use pocketmine\event\block\BlockFormEvent; use pocketmine\event\block\BlockSpreadEvent; use pocketmine\item\Item; use pocketmine\math\AxisAlignedBB; @@ -363,12 +363,8 @@ abstract class Liquid extends Transparent{ } protected function liquidCollide(Block $cause, Block $result) : bool{ - $ev = new BlockFormEvent($this, $result, $cause); - $ev->call(); - if(!$ev->isCancelled()){ - $world = $this->position->getWorld(); - $world->setBlock($this->position, $ev->getNewState()); - $world->addSound($this->position->add(0.5, 0.5, 0.5), new FizzSound(2.6 + (lcg_value() - lcg_value()) * 0.8)); + if(BlockEventHelper::form($this, $result, $cause)){ + $this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new FizzSound(2.6 + (lcg_value() - lcg_value()) * 0.8)); } return true; } diff --git a/src/block/Mycelium.php b/src/block/Mycelium.php index c87f89367..08b0b3e75 100644 --- a/src/block/Mycelium.php +++ b/src/block/Mycelium.php @@ -23,8 +23,8 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\DirtType; -use pocketmine\event\block\BlockSpreadEvent; use pocketmine\item\Item; use pocketmine\math\Facing; use function mt_rand; @@ -54,11 +54,7 @@ class Mycelium extends Opaque{ $block = $world->getBlockAt($x, $y, $z); if($block instanceof Dirt && $block->getDirtType()->equals(DirtType::NORMAL())){ if($block->getSide(Facing::UP) instanceof Transparent){ - $ev = new BlockSpreadEvent($block, $this, VanillaBlocks::MYCELIUM()); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($block->position, $ev->getNewState()); - } + BlockEventHelper::spread($block, VanillaBlocks::MYCELIUM(), $this); } } } diff --git a/src/block/NetherWartPlant.php b/src/block/NetherWartPlant.php index 6a8fe1f7a..d7e587441 100644 --- a/src/block/NetherWartPlant.php +++ b/src/block/NetherWartPlant.php @@ -23,9 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\FortuneDropHelper; use pocketmine\data\runtime\RuntimeDataDescriber; -use pocketmine\event\block\BlockGrowEvent; use pocketmine\item\Item; use pocketmine\math\Facing; use pocketmine\math\Vector3; @@ -76,11 +76,7 @@ class NetherWartPlant extends Flowable{ if($this->age < self::MAX_AGE && mt_rand(0, 10) === 0){ //Still growing $block = clone $this; $block->age++; - $ev = new BlockGrowEvent($this, $block); - $ev->call(); - if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::grow($this, $block, null); } } diff --git a/src/block/SnowLayer.php b/src/block/SnowLayer.php index f561c8ff5..05fc88421 100644 --- a/src/block/SnowLayer.php +++ b/src/block/SnowLayer.php @@ -23,11 +23,11 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\Fallable; use pocketmine\block\utils\FallableTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; -use pocketmine\event\block\BlockMeltEvent; use pocketmine\item\Item; use pocketmine\item\VanillaItems; use pocketmine\math\AxisAlignedBB; @@ -105,11 +105,7 @@ class SnowLayer extends Flowable implements Fallable{ public function onRandomTick() : void{ $world = $this->position->getWorld(); if($world->getBlockLightAt($this->position->x, $this->position->y, $this->position->z) >= 12){ - $ev = new BlockMeltEvent($this, VanillaBlocks::AIR()); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::melt($this, VanillaBlocks::AIR()); } } diff --git a/src/block/Stem.php b/src/block/Stem.php index 252b28aeb..7223572dd 100644 --- a/src/block/Stem.php +++ b/src/block/Stem.php @@ -23,8 +23,8 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\data\runtime\RuntimeDataDescriber; -use pocketmine\event\block\BlockGrowEvent; use pocketmine\item\Item; use pocketmine\math\Facing; use function array_rand; @@ -64,11 +64,7 @@ abstract class Stem extends Crops{ if($this->age < self::MAX_AGE){ $block = clone $this; ++$block->age; - $ev = new BlockGrowEvent($this, $block); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::grow($this, $block, null); }else{ $grow = $this->getPlant(); foreach(Facing::HORIZONTAL as $side){ @@ -80,12 +76,7 @@ abstract class Stem extends Crops{ $facing = Facing::HORIZONTAL[array_rand(Facing::HORIZONTAL)]; $side = $this->getSide($facing); if($side->getTypeId() === BlockTypeIds::AIR && $side->getSide(Facing::DOWN)->hasTypeTag(BlockTypeTags::DIRT)){ - $ev = new BlockGrowEvent($side, $grow); - $ev->call(); - if(!$ev->isCancelled()){ - $world->setBlock($this->position, $this->setFacing($facing)); - $world->setBlock($side->position, $ev->getNewState()); - } + BlockEventHelper::grow($side, $grow, null); } } } diff --git a/src/block/Sugarcane.php b/src/block/Sugarcane.php index 4cc5989a7..3757e4577 100644 --- a/src/block/Sugarcane.php +++ b/src/block/Sugarcane.php @@ -23,8 +23,8 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\data\runtime\RuntimeDataDescriber; -use pocketmine\event\block\BlockGrowEvent; use pocketmine\item\Fertilizer; use pocketmine\item\Item; use pocketmine\math\Facing; @@ -60,13 +60,11 @@ class Sugarcane extends Flowable{ } $b = $world->getBlockAt($pos->x, $pos->y + $y, $pos->z); if($b->getTypeId() === BlockTypeIds::AIR){ - $ev = new BlockGrowEvent($b, VanillaBlocks::SUGARCANE(), $player); - $ev->call(); - if($ev->isCancelled()){ + if(BlockEventHelper::grow($b, VanillaBlocks::SUGARCANE(), $player)){ + $grew = true; + }else{ break; } - $world->setBlock($b->position, $ev->getNewState()); - $grew = true; }elseif(!$b->hasSameTypeId($this)){ break; } diff --git a/src/block/SweetBerryBush.php b/src/block/SweetBerryBush.php index 5ead39390..ef1169df5 100644 --- a/src/block/SweetBerryBush.php +++ b/src/block/SweetBerryBush.php @@ -23,11 +23,11 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\FortuneDropHelper; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\entity\Entity; use pocketmine\entity\Living; -use pocketmine\event\block\BlockGrowEvent; use pocketmine\event\entity\EntityDamageByBlockEvent; use pocketmine\item\Fertilizer; use pocketmine\item\Item; @@ -87,15 +87,9 @@ class SweetBerryBush extends Flowable{ if($this->age < self::STAGE_MATURE && $item instanceof Fertilizer){ $block = clone $this; $block->age++; - - $ev = new BlockGrowEvent($this, $block, $player); - $ev->call(); - - if(!$ev->isCancelled()){ - $world->setBlock($this->position, $ev->getNewState()); + if(BlockEventHelper::grow($this, $block, $player)){ $item->pop(); } - }elseif(($dropAmount = $this->getBerryDropAmount()) > 0){ $world->setBlock($this->position, $this->setAge(self::STAGE_BUSH_NO_BERRIES)); $world->dropItem($this->position, $this->asItem()->setCount($dropAmount)); @@ -133,11 +127,7 @@ class SweetBerryBush extends Flowable{ if($this->age < self::STAGE_MATURE && mt_rand(0, 2) === 1){ $block = clone $this; ++$block->age; - $ev = new BlockGrowEvent($this, $block); - $ev->call(); - if(!$ev->isCancelled()){ - $this->position->getWorld()->setBlock($this->position, $ev->getNewState()); - } + BlockEventHelper::grow($this, $block, null); } } diff --git a/src/block/utils/BlockEventHelper.php b/src/block/utils/BlockEventHelper.php new file mode 100644 index 000000000..fc9b06d13 --- /dev/null +++ b/src/block/utils/BlockEventHelper.php @@ -0,0 +1,115 @@ +call(); + if($ev->isCancelled()){ + return false; + } + $newState = $ev->getNewState(); + } + + $position = $oldState->getPosition(); + $position->getWorld()->setBlock($position, $newState); + return true; + } + + public static function spread(Block $oldState, Block $newState, Block $source) : bool{ + if(BlockSpreadEvent::hasHandlers()){ + $ev = new BlockSpreadEvent($oldState, $source, $newState); + $ev->call(); + if($ev->isCancelled()){ + return false; + } + $newState = $ev->getNewState(); + } + + $position = $oldState->getPosition(); + $position->getWorld()->setBlock($position, $newState); + return true; + } + + public static function form(Block $oldState, Block $newState, Block $cause) : bool{ + if(BlockFormEvent::hasHandlers()){ + $ev = new BlockFormEvent($oldState, $newState, $cause); + $ev->call(); + if($ev->isCancelled()){ + return false; + } + $newState = $ev->getNewState(); + } + + $position = $oldState->getPosition(); + $position->getWorld()->setBlock($position, $newState); + return true; + } + + public static function melt(Block $oldState, Block $newState) : bool{ + if(BlockMeltEvent::hasHandlers()){ + $ev = new BlockMeltEvent($oldState, $newState); + $ev->call(); + if($ev->isCancelled()){ + return false; + } + $newState = $ev->getNewState(); + } + + $position = $oldState->getPosition(); + $position->getWorld()->setBlock($position, $newState); + return true; + } + + public static function die(Block $oldState, Block $newState) : bool{ + if(BlockDeathEvent::hasHandlers()){ + $ev = new BlockDeathEvent($oldState, $newState); + $ev->call(); + if($ev->isCancelled()){ + return false; + } + $newState = $ev->getNewState(); + } + + $position = $oldState->getPosition(); + $position->getWorld()->setBlock($position, $newState); + return true; + } +} From c91c8c2f9e915e3ba67216cf77c76cb59de9bda8 Mon Sep 17 00:00:00 2001 From: Dylan T Date: Thu, 3 Aug 2023 14:51:51 +0100 Subject: [PATCH 05/46] Improving performance of small moving entities (e.g. dropped items) (#5954) * World: cache block AABBs directly in the world this removes some indirection when fetching the AABBs, and also allows the AABB cache to live longer than the block cache. In local testing this showed a 10-20% performance improvement, but it was difficult to properly measure. * World: eliminate padding block checks in getCollisionBoxes() this substantially improves the function's performance for small entities. The padding of 1 block in each direction was previously necessary to account for blocks like fences, which might have an AABB larger than the cell containing them. However, by tracking this information in the collisionBoxCache directly, we can avoid the need to check this at the expense of slightly more complex code. This reduces the number of blocks checked for a moving item entity from 27-64 all the way down to 1-8, which is a major improvement. Locally, this change allowed me to simulate 2100 item entities without lag, compared with 1500 on the previous commit. --- src/world/World.php | 79 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 8 deletions(-) diff --git a/src/world/World.php b/src/world/World.php index f556d2e1f..4f266022f 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -200,6 +200,11 @@ class World implements ChunkManager{ * @phpstan-var array> */ private array $blockCache = []; + /** + * @var AxisAlignedBB[][][] chunkHash => [relativeBlockHash => AxisAlignedBB[]] + * @phpstan-var array>> + */ + private array $blockCollisionBoxCache = []; private int $sendTimeTicker = 0; @@ -646,6 +651,7 @@ class World implements ChunkManager{ $this->provider->close(); $this->blockCache = []; + $this->blockCollisionBoxCache = []; $this->unloaded = true; } @@ -1117,6 +1123,7 @@ class World implements ChunkManager{ public function clearCache(bool $force = false) : void{ if($force){ $this->blockCache = []; + $this->blockCollisionBoxCache = []; }else{ $count = 0; foreach($this->blockCache as $list){ @@ -1126,6 +1133,16 @@ class World implements ChunkManager{ break; } } + + $count = 0; + foreach($this->blockCollisionBoxCache as $list){ + $count += count($list); + if($count > 2048){ + //TODO: Is this really the best logic? + $this->blockCollisionBoxCache = []; + break; + } + } } } @@ -1491,25 +1508,53 @@ class World implements ChunkManager{ return $collides; } + /** + * Returns a list of all block AABBs which overlap the full block area at the given coordinates. + * This checks a padding of 1 block around the coordinates to account for oversized AABBs of blocks like fences. + * Larger AABBs (>= 2 blocks on any axis) are not accounted for. + * + * @return AxisAlignedBB[] + */ + private function getCollisionBoxesForCell(int $x, int $y, int $z) : array{ + $block = $this->getBlockAt($x, $y, $z); + $boxes = $block->getCollisionBoxes(); + + $cellBB = AxisAlignedBB::one()->offset($x, $y, $z); + foreach(Facing::ALL as $facing){ + $extraBoxes = $block->getSide($facing)->getCollisionBoxes(); + foreach($extraBoxes as $extraBox){ + if($extraBox->intersectsWith($cellBB)){ + $boxes[] = $extraBox; + } + } + } + + return $boxes; + } + /** * @return AxisAlignedBB[] * @phpstan-return list */ public function getCollisionBoxes(Entity $entity, AxisAlignedBB $bb, bool $entities = true) : array{ - $minX = (int) floor($bb->minX - 1); - $minY = (int) floor($bb->minY - 1); - $minZ = (int) floor($bb->minZ - 1); - $maxX = (int) floor($bb->maxX + 1); - $maxY = (int) floor($bb->maxY + 1); - $maxZ = (int) floor($bb->maxZ + 1); + $minX = (int) floor($bb->minX); + $minY = (int) floor($bb->minY); + $minZ = (int) floor($bb->minZ); + $maxX = (int) floor($bb->maxX); + $maxY = (int) floor($bb->maxY); + $maxZ = (int) floor($bb->maxZ); $collides = []; for($z = $minZ; $z <= $maxZ; ++$z){ for($x = $minX; $x <= $maxX; ++$x){ + $chunkPosHash = World::chunkHash($x >> Chunk::COORD_BIT_SIZE, $z >> Chunk::COORD_BIT_SIZE); for($y = $minY; $y <= $maxY; ++$y){ - $block = $this->getBlockAt($x, $y, $z); - foreach($block->getCollisionBoxes() as $blockBB){ + $relativeBlockHash = World::chunkBlockHash($x, $y, $z); + + $boxes = $this->blockCollisionBoxCache[$chunkPosHash][$relativeBlockHash] ??= $this->getCollisionBoxesForCell($x, $y, $z); + + foreach($boxes as $blockBB){ if($blockBB->intersectsWith($bb)){ $collides[] = $blockBB; } @@ -1858,6 +1903,21 @@ class World implements ChunkManager{ $relativeBlockHash = World::chunkBlockHash($x, $y, $z); unset($this->blockCache[$chunkHash][$relativeBlockHash]); + unset($this->blockCollisionBoxCache[$chunkHash][$relativeBlockHash]); + //blocks like fences have collision boxes that reach into neighbouring blocks, so we need to invalidate the + //caches for those blocks as well + foreach([ + [0, 0, 1], + [0, 0, -1], + [0, 1, 0], + [0, -1, 0], + [1, 0, 0], + [-1, 0, 0] + ] as [$offsetX, $offsetY, $offsetZ]){ + $sideChunkPosHash = World::chunkHash(($x + $offsetX) >> Chunk::COORD_BIT_SIZE, ($z + $offsetZ) >> Chunk::COORD_BIT_SIZE); + $sideChunkBlockHash = World::chunkBlockHash($x + $offsetX, $y + $offsetY, $z + $offsetZ); + unset($this->blockCollisionBoxCache[$sideChunkPosHash][$sideChunkBlockHash]); + } if(!isset($this->changedBlocks[$chunkHash])){ $this->changedBlocks[$chunkHash] = []; @@ -2454,6 +2514,7 @@ class World implements ChunkManager{ $this->chunks[$chunkHash] = $chunk; unset($this->blockCache[$chunkHash]); + unset($this->blockCollisionBoxCache[$chunkHash]); unset($this->changedBlocks[$chunkHash]); $chunk->setTerrainDirty(); $this->markTickingChunkForRecheck($chunkX, $chunkZ); //this replacement chunk may not meet the conditions for ticking @@ -2733,6 +2794,7 @@ class World implements ChunkManager{ } $this->chunks[$chunkHash] = $chunk; unset($this->blockCache[$chunkHash]); + unset($this->blockCollisionBoxCache[$chunkHash]); $this->initChunk($x, $z, $chunkData); @@ -2887,6 +2949,7 @@ class World implements ChunkManager{ unset($this->chunks[$chunkHash]); unset($this->blockCache[$chunkHash]); + unset($this->blockCollisionBoxCache[$chunkHash]); unset($this->changedBlocks[$chunkHash]); unset($this->registeredTickingChunks[$chunkHash]); $this->markTickingChunkForRecheck($x, $z); From 6ac45526f9b58a068d0be0ffd9fa1bce771f8eb5 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 3 Aug 2023 16:46:16 +0100 Subject: [PATCH 06/46] Use new features in pocketmine/math 1.0.0 --- composer.json | 2 +- composer.lock | 58 ++++++++++++++++----------------- src/block/MobHead.php | 17 +++++----- src/world/World.php | 9 +---- src/world/light/LightUpdate.php | 16 +++------ 5 files changed, 44 insertions(+), 58 deletions(-) diff --git a/composer.json b/composer.json index 5cb32d2ac..f191086ca 100644 --- a/composer.json +++ b/composer.json @@ -43,7 +43,7 @@ "pocketmine/errorhandler": "^0.6.0", "pocketmine/locale-data": "~2.19.0", "pocketmine/log": "^0.4.0", - "pocketmine/math": "^0.4.0", + "pocketmine/math": "~1.0.0", "pocketmine/nbt": "^0.3.2", "pocketmine/raklib": "^0.15.0", "pocketmine/raklib-ipc": "^0.2.0", diff --git a/composer.lock b/composer.lock index a97b576ee..40bdcb23d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ccd20e7656bc05ec2acd8e28aad9fcf2", + "content-hash": "63ecdcea88960659a6977300a0ea79b3", "packages": [ { "name": "adhocore/json-comment", @@ -200,16 +200,16 @@ }, { "name": "pocketmine/bedrock-protocol", - "version": "23.0.2+bedrock-1.20.10", + "version": "23.0.3+bedrock-1.20.10", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "69a309a2dd7dcf3ec8c316385b866397e8c2cbfd" + "reference": "e4157c7af3f91e1b08fe21be171eb73dad7029e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/69a309a2dd7dcf3ec8c316385b866397e8c2cbfd", - "reference": "69a309a2dd7dcf3ec8c316385b866397e8c2cbfd", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/e4157c7af3f91e1b08fe21be171eb73dad7029e9", + "reference": "e4157c7af3f91e1b08fe21be171eb73dad7029e9", "shasum": "" }, "require": { @@ -218,8 +218,8 @@ "php": "^8.0", "pocketmine/binaryutils": "^0.2.0", "pocketmine/color": "^0.2.0 || ^0.3.0", - "pocketmine/math": "^0.3.0 || ^0.4.0", - "pocketmine/nbt": "^0.3.0", + "pocketmine/math": "^0.3.0 || ^0.4.0 || ^1.0.0", + "pocketmine/nbt": "^0.3.0 || ^1.0.0", "ramsey/uuid": "^4.1" }, "require-dev": { @@ -241,9 +241,9 @@ "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", "support": { "issues": "https://github.com/pmmp/BedrockProtocol/issues", - "source": "https://github.com/pmmp/BedrockProtocol/tree/23.0.2+bedrock-1.20.10" + "source": "https://github.com/pmmp/BedrockProtocol/tree/23.0.3+bedrock-1.20.10" }, - "time": "2023-07-24T15:35:36+00:00" + "time": "2023-08-03T15:30:52+00:00" }, { "name": "pocketmine/binaryutils", @@ -480,16 +480,16 @@ }, { "name": "pocketmine/math", - "version": "0.4.3", + "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/pmmp/Math.git", - "reference": "47a243d320b01c8099d65309967934c188111549" + "reference": "dc132d93595b32e9f210d78b3c8d43c662a5edbf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/Math/zipball/47a243d320b01c8099d65309967934c188111549", - "reference": "47a243d320b01c8099d65309967934c188111549", + "url": "https://api.github.com/repos/pmmp/Math/zipball/dc132d93595b32e9f210d78b3c8d43c662a5edbf", + "reference": "dc132d93595b32e9f210d78b3c8d43c662a5edbf", "shasum": "" }, "require": { @@ -498,7 +498,7 @@ }, "require-dev": { "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "1.8.2", + "phpstan/phpstan": "~1.10.3", "phpstan/phpstan-strict-rules": "^1.0", "phpunit/phpunit": "^8.5 || ^9.5" }, @@ -515,9 +515,9 @@ "description": "PHP library containing math related code used in PocketMine-MP", "support": { "issues": "https://github.com/pmmp/Math/issues", - "source": "https://github.com/pmmp/Math/tree/0.4.3" + "source": "https://github.com/pmmp/Math/tree/1.0.0" }, - "time": "2022-08-25T18:43:37+00:00" + "time": "2023-08-03T12:56:33+00:00" }, { "name": "pocketmine/nbt", @@ -1541,16 +1541,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "10.1.2", + "version": "10.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "db1497ec8dd382e82c962f7abbe0320e4882ee4e" + "reference": "be1fe461fdc917de2a29a452ccf2657d325b443d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/db1497ec8dd382e82c962f7abbe0320e4882ee4e", - "reference": "db1497ec8dd382e82c962f7abbe0320e4882ee4e", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/be1fe461fdc917de2a29a452ccf2657d325b443d", + "reference": "be1fe461fdc917de2a29a452ccf2657d325b443d", "shasum": "" }, "require": { @@ -1607,7 +1607,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.2" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.3" }, "funding": [ { @@ -1615,7 +1615,7 @@ "type": "github" } ], - "time": "2023-05-22T09:04:27+00:00" + "time": "2023-07-26T13:45:28+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1861,16 +1861,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.2.6", + "version": "10.2.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "1c17815c129f133f3019cc18e8d0c8622e6d9bcd" + "reference": "a215d9ee8bac1733796e4ddff3306811f14414e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1c17815c129f133f3019cc18e8d0c8622e6d9bcd", - "reference": "1c17815c129f133f3019cc18e8d0c8622e6d9bcd", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a215d9ee8bac1733796e4ddff3306811f14414e5", + "reference": "a215d9ee8bac1733796e4ddff3306811f14414e5", "shasum": "" }, "require": { @@ -1895,7 +1895,7 @@ "sebastian/diff": "^5.0", "sebastian/environment": "^6.0", "sebastian/exporter": "^5.0", - "sebastian/global-state": "^6.0", + "sebastian/global-state": "^6.0.1", "sebastian/object-enumerator": "^5.0", "sebastian/recursion-context": "^5.0", "sebastian/type": "^4.0", @@ -1942,7 +1942,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.2.6" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.2.7" }, "funding": [ { @@ -1958,7 +1958,7 @@ "type": "tidelift" } ], - "time": "2023-07-17T12:08:28+00:00" + "time": "2023-08-02T06:46:08+00:00" }, { "name": "sebastian/cli-parser", diff --git a/src/block/MobHead.php b/src/block/MobHead.php index 1b3f2d88f..96cd1cf34 100644 --- a/src/block/MobHead.php +++ b/src/block/MobHead.php @@ -113,14 +113,15 @@ class MobHead extends Flowable{ * @return AxisAlignedBB[] */ protected function recalculateCollisionBoxes() : array{ - $collisionBox = AxisAlignedBB::one()->contract(0.25, 0, 0.25)->trim(Facing::UP, 0.5); - return match($this->facing){ - Facing::NORTH => [$collisionBox->offset(0, 0.25, 0.25)], - Facing::SOUTH => [$collisionBox->offset(0, 0.25, -0.25)], - Facing::WEST => [$collisionBox->offset(0.25, 0.25, 0)], - Facing::EAST => [$collisionBox->offset(-0.25, 0.25, 0)], - default => [$collisionBox] - }; + $collisionBox = AxisAlignedBB::one() + ->contract(0.25, 0, 0.25) + ->trim(Facing::UP, 0.5); + if($this->facing !== Facing::UP){ + $collisionBox = $collisionBox + ->offsetTowards(Facing::opposite($this->facing), 0.25) + ->offsetTowards(Facing::UP, 0.25); + } + return [$collisionBox]; } public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ diff --git a/src/world/World.php b/src/world/World.php index 4f266022f..49025b948 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -1906,14 +1906,7 @@ class World implements ChunkManager{ unset($this->blockCollisionBoxCache[$chunkHash][$relativeBlockHash]); //blocks like fences have collision boxes that reach into neighbouring blocks, so we need to invalidate the //caches for those blocks as well - foreach([ - [0, 0, 1], - [0, 0, -1], - [0, 1, 0], - [0, -1, 0], - [1, 0, 0], - [-1, 0, 0] - ] as [$offsetX, $offsetY, $offsetZ]){ + foreach(Facing::OFFSET as [$offsetX, $offsetY, $offsetZ]){ $sideChunkPosHash = World::chunkHash(($x + $offsetX) >> Chunk::COORD_BIT_SIZE, ($z + $offsetZ) >> Chunk::COORD_BIT_SIZE); $sideChunkBlockHash = World::chunkBlockHash($x + $offsetX, $y + $offsetY, $z + $offsetZ); unset($this->blockCollisionBoxCache[$sideChunkPosHash][$sideChunkBlockHash]); diff --git a/src/world/light/LightUpdate.php b/src/world/light/LightUpdate.php index 1a82bdcef..58ac0a9dc 100644 --- a/src/world/light/LightUpdate.php +++ b/src/world/light/LightUpdate.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\world\light; +use pocketmine\math\Facing; use pocketmine\world\format\LightArray; use pocketmine\world\format\SubChunk; use pocketmine\world\utils\SubChunkExplorer; @@ -33,15 +34,6 @@ use function max; //TODO: make light updates asynchronous abstract class LightUpdate{ - private const ADJACENTS = [ - [ 1, 0, 0], - [-1, 0, 0], - [ 0, 1, 0], - [ 0, -1, 0], - [ 0, 0, 1], - [ 0, 0, -1] - ]; - public const BASE_LIGHT_FILTER = 1; /** @@ -78,7 +70,7 @@ abstract class LightUpdate{ protected function getHighestAdjacentLight(int $x, int $y, int $z) : int{ $adjacent = 0; - foreach(self::ADJACENTS as [$ox, $oy, $oz]){ + foreach(Facing::OFFSET as [$ox, $oy, $oz]){ if(($adjacent = max($adjacent, $this->getEffectiveLight($x + $ox, $y + $oy, $z + $oz))) === 15){ break; } @@ -123,7 +115,7 @@ abstract class LightUpdate{ $touched++; [$x, $y, $z, $oldAdjacentLight] = $context->removalQueue->dequeue(); - foreach(self::ADJACENTS as [$ox, $oy, $oz]){ + foreach(Facing::OFFSET as [$ox, $oy, $oz]){ $cx = $x + $ox; $cy = $y + $oy; $cz = $z + $oz; @@ -163,7 +155,7 @@ abstract class LightUpdate{ continue; } - foreach(self::ADJACENTS as [$ox, $oy, $oz]){ + foreach(Facing::OFFSET as [$ox, $oy, $oz]){ $cx = $x + $ox; $cy = $y + $oy; $cz = $z + $oz; From 1cf508abdbda46ab178da7637aaeb0b892e401ac Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 3 Aug 2023 16:51:09 +0100 Subject: [PATCH 07/46] World: use Facing::OFFSET in getHighestAdjacentLight() --- src/world/World.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/world/World.php b/src/world/World.php index 49025b948..4d6cad154 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -1727,14 +1727,10 @@ class World implements ChunkManager{ */ private function getHighestAdjacentLight(int $x, int $y, int $z, \Closure $lightGetter) : int{ $max = 0; - foreach([ - [$x + 1, $y, $z], - [$x - 1, $y, $z], - [$x, $y + 1, $z], - [$x, $y - 1, $z], - [$x, $y, $z + 1], - [$x, $y, $z - 1] - ] as [$x1, $y1, $z1]){ + foreach(Facing::OFFSET as [$offsetX, $offsetY, $offsetZ]){ + $x1 = $x + $offsetX; + $y1 = $y + $offsetY; + $z1 = $z + $offsetZ; if( !$this->isInWorld($x1, $y1, $z1) || ($chunk = $this->getChunk($x1 >> Chunk::COORD_BIT_SIZE, $z1 >> Chunk::COORD_BIT_SIZE)) === null || From 515f8eae4ca2b7b0a3e9251904dfe0f3f688a777 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 7 Aug 2023 17:05:45 +0100 Subject: [PATCH 08/46] =?UTF-8?q?=C3=82ResourcePackManager:=20allow=20sett?= =?UTF-8?q?ing=20force=5Fresources=20from=20a=20plugin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/resourcepacks/ResourcePackManager.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/resourcepacks/ResourcePackManager.php b/src/resourcepacks/ResourcePackManager.php index 6429ac06b..2df6750de 100644 --- a/src/resourcepacks/ResourcePackManager.php +++ b/src/resourcepacks/ResourcePackManager.php @@ -154,6 +154,13 @@ class ResourcePackManager{ return $this->serverForceResources; } + /** + * Sets whether players must accept resource packs in order to join. + */ + public function setResourcePacksRequired(bool $value) : void{ + $this->serverForceResources = $value; + } + /** * Returns an array of resource packs in use, sorted in order of priority. * @return ResourcePack[] From 77dfbc4e234147aa263bf4efcd8c4edcdef0a31c Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Wed, 9 Aug 2023 13:33:33 +0300 Subject: [PATCH 09/46] Implemented pink petals (#5940) --- src/block/BlockTypeIds.php | 3 +- src/block/PinkPetals.php | 119 ++++++++++++++++++ src/block/VanillaBlocks.php | 2 + .../convert/BlockObjectToStateSerializer.php | 6 + .../BlockStateToObjectDeserializer.php | 8 ++ src/item/StringToItemParser.php | 1 + .../block_factory_consistency_check.json | 2 +- 7 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 src/block/PinkPetals.php diff --git a/src/block/BlockTypeIds.php b/src/block/BlockTypeIds.php index fe2101e1f..dc86490c3 100644 --- a/src/block/BlockTypeIds.php +++ b/src/block/BlockTypeIds.php @@ -736,8 +736,9 @@ final class BlockTypeIds{ public const SMALL_DRIPLEAF = 10706; public const BIG_DRIPLEAF_HEAD = 10707; public const BIG_DRIPLEAF_STEM = 10708; + public const PINK_PETALS = 10709; - public const FIRST_UNUSED_BLOCK_ID = 10709; + public const FIRST_UNUSED_BLOCK_ID = 10710; private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID; diff --git a/src/block/PinkPetals.php b/src/block/PinkPetals.php new file mode 100644 index 000000000..9271e7ddc --- /dev/null +++ b/src/block/PinkPetals.php @@ -0,0 +1,119 @@ +horizontalFacing($this->facing); + $w->boundedInt(2, self::MIN_COUNT, self::MAX_COUNT, $this->count); + } + + public function getCount() : int{ + return $this->count; + } + + /** @return $this */ + public function setCount(int $count) : self{ + if($count < self::MIN_COUNT || $count > self::MAX_COUNT){ + throw new \InvalidArgumentException("Count must be in range " . self::MIN_COUNT . " ... " . self::MAX_COUNT); + } + $this->count = $count; + return $this; + } + + private function canBeSupportedAt(Block $block) : bool{ + $supportBlock = $block->getSide(Facing::DOWN); + //TODO: Moss block + return $supportBlock->hasTypeTag(BlockTypeTags::DIRT) || $supportBlock->hasTypeTag(BlockTypeTags::MUD); + } + + public function onNearbyBlockChange() : void{ + if(!$this->canBeSupportedAt($this)){ + $this->position->getWorld()->useBreakOn($this->position); + } + } + + public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{ + return ($blockReplace instanceof PinkPetals && $blockReplace->getCount() < self::MAX_COUNT) || parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock); + } + + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + if(!$this->canBeSupportedAt($this)){ + return false; + } + if($blockReplace instanceof PinkPetals && $blockReplace->getCount() < self::MAX_COUNT){ + $this->count = $blockReplace->getCount() + 1; + $this->facing = $blockReplace->getFacing(); + }elseif($player !== null){ + $this->facing = Facing::opposite($player->getHorizontalFacing()); + } + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } + + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + if($item instanceof Fertilizer){ + $grew = false; + if($this->count < self::MAX_COUNT){ + $grew = BlockEventHelper::grow($this, (clone $this)->setCount($this->count + 1), $player); + }else{ + $this->position->getWorld()->dropItem($this->position->add(0, 0.5, 0), $this->asItem()); + $grew = true; + } + if($grew){ + $item->pop(); + return true; + } + } + return false; + } + + public function getFlameEncouragement() : int{ + return 60; + } + + public function getFlammability() : int{ + return 100; + } + + public function getDropsForCompatibleTool(Item $item) : array{ + return [$this->asItem()->setCount($this->count)]; + } +} diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index 9f2996abd..d733b06c2 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -573,6 +573,7 @@ use function mb_strtolower; * @method static PackedIce PACKED_ICE() * @method static Opaque PACKED_MUD() * @method static DoublePlant PEONY() + * @method static PinkPetals PINK_PETALS() * @method static Flower PINK_TULIP() * @method static Podzol PODZOL() * @method static Opaque POLISHED_ANDESITE() @@ -840,6 +841,7 @@ final class VanillaBlocks{ self::register("lilac", new DoublePlant(new BID(Ids::LILAC), "Lilac", new Info(BreakInfo::instant()))); self::register("rose_bush", new DoublePlant(new BID(Ids::ROSE_BUSH), "Rose Bush", new Info(BreakInfo::instant()))); self::register("peony", new DoublePlant(new BID(Ids::PEONY), "Peony", new Info(BreakInfo::instant()))); + self::register("pink_petals", new PinkPetals(new BID(Ids::PINK_PETALS), "Pink Petals", new Info(BreakInfo::instant()))); self::register("double_tallgrass", new DoubleTallGrass(new BID(Ids::DOUBLE_TALLGRASS), "Double Tallgrass", new Info(BreakInfo::instant(ToolType::SHEARS, 1)))); self::register("large_fern", new DoubleTallGrass(new BID(Ids::LARGE_FERN), "Large Fern", new Info(BreakInfo::instant(ToolType::SHEARS, 1)))); self::register("dragon_egg", new DragonEgg(new BID(Ids::DRAGON_EGG), "Dragon Egg", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD())))); diff --git a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php index 8ec9a1ddc..6ffc1c55d 100644 --- a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php +++ b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php @@ -100,6 +100,7 @@ use pocketmine\block\MobHead; use pocketmine\block\NetherPortal; use pocketmine\block\NetherVines; use pocketmine\block\NetherWartPlant; +use pocketmine\block\PinkPetals; use pocketmine\block\Potato; use pocketmine\block\PoweredRail; use pocketmine\block\PumpkinStem; @@ -1364,6 +1365,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->map(Blocks::ORANGE_TULIP(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_TULIP_ORANGE)); $this->map(Blocks::OXEYE_DAISY(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_OXEYE)); $this->map(Blocks::PEONY(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, StringValues::DOUBLE_PLANT_TYPE_PAEONIA, Writer::create(Ids::DOUBLE_PLANT))); + $this->map(Blocks::PINK_PETALS(), function(PinkPetals $block) : Writer{ + return Writer::create(Ids::PINK_PETALS) + ->writeLegacyHorizontalFacing($block->getFacing()) + ->writeInt(StateNames::GROWTH, $block->getCount() - 1); + }); $this->map(Blocks::PINK_TULIP(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_TULIP_PINK)); $this->map(Blocks::POLISHED_ANDESITE(), fn() => Helper::encodeStone(StringValues::STONE_TYPE_ANDESITE_SMOOTH)); $this->map(Blocks::POLISHED_ANDESITE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_POLISHED_ANDESITE)); diff --git a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php index 08c9b5914..f6b8699a9 100644 --- a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php +++ b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php @@ -28,6 +28,7 @@ use pocketmine\block\Block; use pocketmine\block\CaveVines; use pocketmine\block\ChorusFlower; use pocketmine\block\Light; +use pocketmine\block\PinkPetals; use pocketmine\block\Slab; use pocketmine\block\Stair; use pocketmine\block\SweetBerryBush; @@ -1175,6 +1176,13 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapSlab(Ids::OXIDIZED_CUT_COPPER_SLAB, Ids::OXIDIZED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::OXIDIZED())); $this->mapStairs(Ids::OXIDIZED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::OXIDIZED())); $this->map(Ids::PEARLESCENT_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::PEARLESCENT())->setAxis($in->readPillarAxis())); + $this->map(Ids::PINK_PETALS, function(Reader $in) : Block{ + //Pink petals only uses 0-3, but GROWTH state can go up to 7 + $growth = $in->readBoundedInt(StateNames::GROWTH, 0, 7); + return Blocks::PINK_PETALS() + ->setFacing($in->readLegacyHorizontalFacing()) + ->setCount(min($growth + 1, PinkPetals::MAX_COUNT)); + }); $this->mapStairs(Ids::POLISHED_ANDESITE_STAIRS, fn() => Blocks::POLISHED_ANDESITE_STAIRS()); $this->map(Ids::POLISHED_BASALT, function(Reader $in) : Block{ return Blocks::POLISHED_BASALT() diff --git a/src/item/StringToItemParser.php b/src/item/StringToItemParser.php index ea9923c21..c8be97cad 100644 --- a/src/item/StringToItemParser.php +++ b/src/item/StringToItemParser.php @@ -856,6 +856,7 @@ final class StringToItemParser extends StringToTParser{ $result->registerBlock("packed_ice", fn() => Blocks::PACKED_ICE()); $result->registerBlock("packed_mud", fn() => Blocks::PACKED_MUD()); $result->registerBlock("peony", fn() => Blocks::PEONY()); + $result->registerBlock("pink_petals", fn() => Blocks::PINK_PETALS()); $result->registerBlock("pink_tulip", fn() => Blocks::PINK_TULIP()); $result->registerBlock("piglin_head", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::PIGLIN())); $result->registerBlock("plank", fn() => Blocks::OAK_PLANKS()); diff --git a/tests/phpunit/block/block_factory_consistency_check.json b/tests/phpunit/block/block_factory_consistency_check.json index 2e9fa0555..c12010564 100644 --- a/tests/phpunit/block/block_factory_consistency_check.json +++ b/tests/phpunit/block/block_factory_consistency_check.json @@ -1 +1 @@ -{"knownStates":{"???":[2624010],"Acacia Button":[2560272,2560273,2560274,2560275,2560276,2560277,2560280,2560281,2560282,2560283,2560284,2560285],"Acacia Door":[2560512,2560513,2560514,2560515,2560516,2560517,2560518,2560519,2560520,2560521,2560522,2560523,2560524,2560525,2560526,2560527,2560528,2560529,2560530,2560531,2560532,2560533,2560534,2560535,2560536,2560537,2560538,2560539,2560540,2560541,2560542,2560543],"Acacia Fence":[2560787],"Acacia Fence Gate":[2561040,2561041,2561042,2561043,2561044,2561045,2561046,2561047,2561048,2561049,2561050,2561051,2561052,2561053,2561054,2561055],"Acacia Leaves":[2561300,2561301,2561302,2561303],"Acacia Log":[2561554,2561555,2561556,2561557,2561558,2561559],"Acacia Planks":[2561815],"Acacia Pressure Plate":[2562072,2562073],"Acacia Sapling":[2562328,2562329],"Acacia Sign":[2562576,2562577,2562578,2562579,2562580,2562581,2562582,2562583,2562584,2562585,2562586,2562587,2562588,2562589,2562590,2562591],"Acacia Slab":[2562841,2562842,2562843],"Acacia Stairs":[2563096,2563097,2563098,2563099,2563100,2563101,2563102,2563103],"Acacia Trapdoor":[2563344,2563345,2563346,2563347,2563348,2563349,2563350,2563351,2563352,2563353,2563354,2563355,2563356,2563357,2563358,2563359],"Acacia Wall Sign":[2563612,2563613,2563614,2563615],"Acacia Wood":[2563866,2563867,2563868,2563869,2563870,2563871],"Actinium":[2594197],"Activator Rail":[2564128,2564129,2564130,2564131,2564132,2564133,2564136,2564137,2564138,2564139,2564140,2564141],"Air":[2560016],"All Sided Mushroom Stem":[2564385],"Allium":[2564642],"Aluminum":[2594454],"Americium":[2594711],"Amethyst":[2698284],"Ancient Debris":[2698541],"Andesite":[2564899],"Andesite Slab":[2565156,2565157,2565158],"Andesite Stairs":[2565408,2565409,2565410,2565411,2565412,2565413,2565414,2565415],"Andesite Wall":[2565632,2565633,2565634,2565635,2565636,2565637,2565638,2565639,2565640,2565641,2565642,2565643,2565644,2565645,2565646,2565647,2565648,2565649,2565650,2565651,2565652,2565653,2565654,2565655,2565656,2565657,2565658,2565659,2565660,2565661,2565662,2565663,2565664,2565665,2565666,2565667,2565668,2565669,2565670,2565671,2565672,2565673,2565674,2565675,2565676,2565677,2565678,2565679,2565680,2565681,2565682,2565683,2565684,2565685,2565686,2565687,2565688,2565689,2565690,2565691,2565692,2565693,2565694,2565695,2565728,2565729,2565730,2565731,2565732,2565733,2565734,2565735,2565736,2565737,2565738,2565739,2565740,2565741,2565742,2565743,2565750,2565760,2565761,2565762,2565763,2565764,2565765,2565766,2565767,2565768,2565769,2565770,2565771,2565772,2565773,2565774,2565775,2565776,2565777,2565778,2565779,2565780,2565781,2565782,2565783,2565784,2565785,2565786,2565787,2565788,2565789,2565790,2565791,2565792,2565793,2565794,2565795,2565796,2565797,2565798,2565799,2565800,2565801,2565802,2565803,2565804,2565805,2565806,2565807,2565808,2565809,2565810,2565811,2565812,2565813,2565814,2565815,2565816,2565817,2565818,2565819,2565820,2565821,2565822,2565823,2565856,2565857,2565858,2565859,2565860,2565861,2565862,2565863,2565864,2565865,2565866,2565867,2565868,2565869,2565870,2565871,2565878],"Antimony":[2594968],"Anvil":[2565921,2565922,2565923,2565925,2565926,2565927,2565929,2565930,2565931,2565933,2565934,2565935],"Argon":[2595225],"Arsenic":[2595482],"Astatine":[2595739],"Azalea Leaves":[2735804,2735805,2735806,2735807],"Azure Bluet":[2566184],"Bamboo":[2566432,2566433,2566435,2566436,2566437,2566439,2566440,2566441,2566443,2566444,2566445,2566447],"Bamboo Sapling":[2566698,2566699],"Banner":[2566912,2566913,2566914,2566915,2566916,2566917,2566918,2566919,2566920,2566921,2566922,2566923,2566924,2566925,2566926,2566927,2566928,2566929,2566930,2566931,2566932,2566933,2566934,2566935,2566936,2566937,2566938,2566939,2566940,2566941,2566942,2566943,2566944,2566945,2566946,2566947,2566948,2566949,2566950,2566951,2566952,2566953,2566954,2566955,2566956,2566957,2566958,2566959,2566960,2566961,2566962,2566963,2566964,2566965,2566966,2566967,2566968,2566969,2566970,2566971,2566972,2566973,2566974,2566975,2566976,2566977,2566978,2566979,2566980,2566981,2566982,2566983,2566984,2566985,2566986,2566987,2566988,2566989,2566990,2566991,2566992,2566993,2566994,2566995,2566996,2566997,2566998,2566999,2567000,2567001,2567002,2567003,2567004,2567005,2567006,2567007,2567008,2567009,2567010,2567011,2567012,2567013,2567014,2567015,2567016,2567017,2567018,2567019,2567020,2567021,2567022,2567023,2567024,2567025,2567026,2567027,2567028,2567029,2567030,2567031,2567032,2567033,2567034,2567035,2567036,2567037,2567038,2567039,2567040,2567041,2567042,2567043,2567044,2567045,2567046,2567047,2567048,2567049,2567050,2567051,2567052,2567053,2567054,2567055,2567056,2567057,2567058,2567059,2567060,2567061,2567062,2567063,2567064,2567065,2567066,2567067,2567068,2567069,2567070,2567071,2567072,2567073,2567074,2567075,2567076,2567077,2567078,2567079,2567080,2567081,2567082,2567083,2567084,2567085,2567086,2567087,2567088,2567089,2567090,2567091,2567092,2567093,2567094,2567095,2567096,2567097,2567098,2567099,2567100,2567101,2567102,2567103,2567104,2567105,2567106,2567107,2567108,2567109,2567110,2567111,2567112,2567113,2567114,2567115,2567116,2567117,2567118,2567119,2567120,2567121,2567122,2567123,2567124,2567125,2567126,2567127,2567128,2567129,2567130,2567131,2567132,2567133,2567134,2567135,2567136,2567137,2567138,2567139,2567140,2567141,2567142,2567143,2567144,2567145,2567146,2567147,2567148,2567149,2567150,2567151,2567152,2567153,2567154,2567155,2567156,2567157,2567158,2567159,2567160,2567161,2567162,2567163,2567164,2567165,2567166,2567167],"Barium":[2595996],"Barrel":[2567200,2567201,2567204,2567205,2567206,2567207,2567208,2567209,2567212,2567213,2567214,2567215],"Barrier":[2567469],"Basalt":[2698796,2698798,2698799],"Beacon":[2567726],"Bed Block":[2567936,2567937,2567938,2567939,2567940,2567941,2567942,2567943,2567944,2567945,2567946,2567947,2567948,2567949,2567950,2567951,2567952,2567953,2567954,2567955,2567956,2567957,2567958,2567959,2567960,2567961,2567962,2567963,2567964,2567965,2567966,2567967,2567968,2567969,2567970,2567971,2567972,2567973,2567974,2567975,2567976,2567977,2567978,2567979,2567980,2567981,2567982,2567983,2567984,2567985,2567986,2567987,2567988,2567989,2567990,2567991,2567992,2567993,2567994,2567995,2567996,2567997,2567998,2567999,2568000,2568001,2568002,2568003,2568004,2568005,2568006,2568007,2568008,2568009,2568010,2568011,2568012,2568013,2568014,2568015,2568016,2568017,2568018,2568019,2568020,2568021,2568022,2568023,2568024,2568025,2568026,2568027,2568028,2568029,2568030,2568031,2568032,2568033,2568034,2568035,2568036,2568037,2568038,2568039,2568040,2568041,2568042,2568043,2568044,2568045,2568046,2568047,2568048,2568049,2568050,2568051,2568052,2568053,2568054,2568055,2568056,2568057,2568058,2568059,2568060,2568061,2568062,2568063,2568064,2568065,2568066,2568067,2568068,2568069,2568070,2568071,2568072,2568073,2568074,2568075,2568076,2568077,2568078,2568079,2568080,2568081,2568082,2568083,2568084,2568085,2568086,2568087,2568088,2568089,2568090,2568091,2568092,2568093,2568094,2568095,2568096,2568097,2568098,2568099,2568100,2568101,2568102,2568103,2568104,2568105,2568106,2568107,2568108,2568109,2568110,2568111,2568112,2568113,2568114,2568115,2568116,2568117,2568118,2568119,2568120,2568121,2568122,2568123,2568124,2568125,2568126,2568127,2568128,2568129,2568130,2568131,2568132,2568133,2568134,2568135,2568136,2568137,2568138,2568139,2568140,2568141,2568142,2568143,2568144,2568145,2568146,2568147,2568148,2568149,2568150,2568151,2568152,2568153,2568154,2568155,2568156,2568157,2568158,2568159,2568160,2568161,2568162,2568163,2568164,2568165,2568166,2568167,2568168,2568169,2568170,2568171,2568172,2568173,2568174,2568175,2568176,2568177,2568178,2568179,2568180,2568181,2568182,2568183,2568184,2568185,2568186,2568187,2568188,2568189,2568190,2568191],"Bedrock":[2568240,2568241],"Beetroot Block":[2568496,2568497,2568498,2568499,2568500,2568501,2568502,2568503],"Bell":[2568752,2568753,2568754,2568755,2568756,2568757,2568758,2568759,2568760,2568761,2568762,2568763,2568764,2568765,2568766,2568767],"Berkelium":[2596253],"Beryllium":[2596510],"Big Dripleaf":[2741200,2741201,2741202,2741203,2741204,2741205,2741206,2741207,2741208,2741209,2741210,2741211,2741212,2741213,2741214,2741215],"Big Dripleaf Stem":[2741460,2741461,2741462,2741463],"Birch Button":[2569008,2569009,2569010,2569011,2569014,2569015,2569016,2569017,2569018,2569019,2569022,2569023],"Birch Door":[2569248,2569249,2569250,2569251,2569252,2569253,2569254,2569255,2569256,2569257,2569258,2569259,2569260,2569261,2569262,2569263,2569264,2569265,2569266,2569267,2569268,2569269,2569270,2569271,2569272,2569273,2569274,2569275,2569276,2569277,2569278,2569279],"Birch Fence":[2569525],"Birch Fence Gate":[2569776,2569777,2569778,2569779,2569780,2569781,2569782,2569783,2569784,2569785,2569786,2569787,2569788,2569789,2569790,2569791],"Birch Leaves":[2570036,2570037,2570038,2570039],"Birch Log":[2570296,2570297,2570298,2570299,2570300,2570301],"Birch Planks":[2570553],"Birch Pressure Plate":[2570810,2570811],"Birch Sapling":[2571066,2571067],"Birch Sign":[2571312,2571313,2571314,2571315,2571316,2571317,2571318,2571319,2571320,2571321,2571322,2571323,2571324,2571325,2571326,2571327],"Birch Slab":[2571580,2571581,2571583],"Birch Stairs":[2571832,2571833,2571834,2571835,2571836,2571837,2571838,2571839],"Birch Trapdoor":[2572080,2572081,2572082,2572083,2572084,2572085,2572086,2572087,2572088,2572089,2572090,2572091,2572092,2572093,2572094,2572095],"Birch Wall Sign":[2572352,2572353,2572354,2572355],"Birch Wood":[2572608,2572609,2572610,2572611,2572612,2572613],"Bismuth":[2596767],"Blackstone":[2699569],"Blackstone Slab":[2699824,2699826,2699827],"Blackstone Stairs":[2700080,2700081,2700082,2700083,2700084,2700085,2700086,2700087],"Blackstone Wall":[2700288,2700289,2700290,2700291,2700292,2700293,2700294,2700295,2700296,2700297,2700298,2700299,2700300,2700301,2700302,2700303,2700304,2700305,2700306,2700307,2700308,2700309,2700310,2700311,2700312,2700313,2700314,2700315,2700316,2700317,2700318,2700319,2700320,2700321,2700322,2700323,2700324,2700325,2700326,2700327,2700328,2700329,2700330,2700331,2700332,2700333,2700334,2700335,2700336,2700337,2700338,2700339,2700340,2700341,2700342,2700343,2700344,2700345,2700346,2700347,2700348,2700349,2700350,2700351,2700388,2700400,2700401,2700402,2700403,2700404,2700405,2700406,2700407,2700408,2700409,2700410,2700411,2700412,2700413,2700414,2700415,2700416,2700417,2700418,2700419,2700420,2700421,2700422,2700423,2700424,2700425,2700426,2700427,2700428,2700429,2700430,2700431,2700432,2700433,2700434,2700435,2700436,2700437,2700438,2700439,2700440,2700441,2700442,2700443,2700444,2700445,2700446,2700447,2700448,2700449,2700450,2700451,2700452,2700453,2700454,2700455,2700456,2700457,2700458,2700459,2700460,2700461,2700462,2700463,2700464,2700465,2700466,2700467,2700468,2700469,2700470,2700471,2700472,2700473,2700474,2700475,2700476,2700477,2700478,2700479,2700516,2700528,2700529,2700530,2700531,2700532,2700533,2700534,2700535,2700536,2700537,2700538,2700539,2700540,2700541,2700542,2700543],"Blast Furnace":[2573120,2573121,2573122,2573123,2573124,2573125,2573126,2573127],"Blue Ice":[2573637],"Blue Orchid":[2573894],"Blue Torch":[2574146,2574147,2574148,2574149,2574150],"Bohrium":[2597024],"Bone Block":[2574408,2574409,2574410],"Bookshelf":[2574665],"Boron":[2597281],"Brewing Stand":[2574920,2574921,2574922,2574923,2574924,2574925,2574926,2574927],"Brick Slab":[2575177,2575178,2575179],"Brick Stairs":[2575432,2575433,2575434,2575435,2575436,2575437,2575438,2575439],"Brick Wall":[2575616,2575617,2575618,2575619,2575620,2575621,2575622,2575623,2575624,2575625,2575626,2575627,2575628,2575629,2575630,2575631,2575645,2575680,2575681,2575682,2575683,2575684,2575685,2575686,2575687,2575688,2575689,2575690,2575691,2575692,2575693,2575694,2575695,2575696,2575697,2575698,2575699,2575700,2575701,2575702,2575703,2575704,2575705,2575706,2575707,2575708,2575709,2575710,2575711,2575712,2575713,2575714,2575715,2575716,2575717,2575718,2575719,2575720,2575721,2575722,2575723,2575724,2575725,2575726,2575727,2575728,2575729,2575730,2575731,2575732,2575733,2575734,2575735,2575736,2575737,2575738,2575739,2575740,2575741,2575742,2575743,2575744,2575745,2575746,2575747,2575748,2575749,2575750,2575751,2575752,2575753,2575754,2575755,2575756,2575757,2575758,2575759,2575773,2575808,2575809,2575810,2575811,2575812,2575813,2575814,2575815,2575816,2575817,2575818,2575819,2575820,2575821,2575822,2575823,2575824,2575825,2575826,2575827,2575828,2575829,2575830,2575831,2575832,2575833,2575834,2575835,2575836,2575837,2575838,2575839,2575840,2575841,2575842,2575843,2575844,2575845,2575846,2575847,2575848,2575849,2575850,2575851,2575852,2575853,2575854,2575855,2575856,2575857,2575858,2575859,2575860,2575861,2575862,2575863,2575864,2575865,2575866,2575867,2575868,2575869,2575870,2575871],"Bricks":[2575950],"Bromine":[2597538],"Brown Mushroom":[2576464],"Brown Mushroom Block":[2576720,2576721,2576722,2576723,2576724,2576725,2576726,2576727,2576728,2576729,2576731],"Cactus":[2576976,2576977,2576978,2576979,2576980,2576981,2576982,2576983,2576984,2576985,2576986,2576987,2576988,2576989,2576990,2576991],"Cadmium":[2597795],"Cake":[2577232,2577233,2577234,2577235,2577237,2577238,2577239],"Cake With Candle":[2729638,2729639],"Cake With Dyed Candle":[2729888,2729889,2729890,2729891,2729892,2729893,2729894,2729895,2729896,2729897,2729898,2729899,2729900,2729901,2729902,2729903,2729904,2729905,2729906,2729907,2729908,2729909,2729910,2729911,2729912,2729913,2729914,2729915,2729916,2729917,2729918,2729919],"Calcite":[2704709],"Calcium":[2598052],"Californium":[2598309],"Candle":[2729120,2729121,2729122,2729123,2729124,2729125,2729126,2729127],"Carbon":[2598566],"Carpet":[2577488,2577489,2577490,2577491,2577492,2577493,2577494,2577495,2577496,2577497,2577498,2577499,2577500,2577501,2577502,2577503],"Carrot Block":[2577744,2577745,2577746,2577747,2577748,2577749,2577750,2577751],"Cartography Table":[2730666],"Carved Pumpkin":[2578004,2578005,2578006,2578007],"Cauldron":[2731694],"Cave Vines":[2736512,2736513,2736514,2736515,2736516,2736517,2736518,2736519,2736520,2736521,2736522,2736523,2736524,2736525,2736526,2736527,2736528,2736529,2736530,2736531,2736532,2736533,2736534,2736535,2736536,2736537,2736544,2736545,2736546,2736547,2736548,2736549,2736550,2736551,2736552,2736553,2736554,2736555,2736556,2736557,2736558,2736559,2736560,2736561,2736562,2736563,2736564,2736565,2736566,2736567,2736568,2736569,2736576,2736577,2736578,2736579,2736580,2736581,2736582,2736583,2736584,2736585,2736586,2736587,2736588,2736589,2736590,2736591,2736592,2736593,2736594,2736595,2736596,2736597,2736598,2736599,2736600,2736601,2736608,2736609,2736610,2736611,2736612,2736613,2736614,2736615,2736616,2736617,2736618,2736619,2736620,2736621,2736622,2736623,2736624,2736625,2736626,2736627,2736628,2736629,2736630,2736631,2736632,2736633],"Cerium":[2598823],"Cesium":[2599080],"Chain":[2734776,2734778,2734779],"Cherry Button":[2737088,2737089,2737090,2737091,2737094,2737095,2737096,2737097,2737098,2737099,2737102,2737103],"Cherry Door":[2737344,2737345,2737346,2737347,2737348,2737349,2737350,2737351,2737352,2737353,2737354,2737355,2737356,2737357,2737358,2737359,2737360,2737361,2737362,2737363,2737364,2737365,2737366,2737367,2737368,2737369,2737370,2737371,2737372,2737373,2737374,2737375],"Cherry Fence":[2737605],"Cherry Fence Gate":[2737856,2737857,2737858,2737859,2737860,2737861,2737862,2737863,2737864,2737865,2737866,2737867,2737868,2737869,2737870,2737871],"Cherry Leaves":[2738116,2738117,2738118,2738119],"Cherry Log":[2738376,2738377,2738378,2738379,2738380,2738381],"Cherry Planks":[2738633],"Cherry Pressure Plate":[2738890,2738891],"Cherry Sign":[2739392,2739393,2739394,2739395,2739396,2739397,2739398,2739399,2739400,2739401,2739402,2739403,2739404,2739405,2739406,2739407],"Cherry Slab":[2739660,2739661,2739663],"Cherry Stairs":[2739912,2739913,2739914,2739915,2739916,2739917,2739918,2739919],"Cherry Trapdoor":[2740160,2740161,2740162,2740163,2740164,2740165,2740166,2740167,2740168,2740169,2740170,2740171,2740172,2740173,2740174,2740175],"Cherry Wall Sign":[2740432,2740433,2740434,2740435],"Cherry Wood":[2740688,2740689,2740690,2740691,2740692,2740693],"Chest":[2578520,2578521,2578522,2578523],"Chiseled Deepslate":[2710106],"Chiseled Nether Bricks":[2710363],"Chiseled Polished Blackstone":[2702139],"Chiseled Quartz Block":[2578776,2578777,2578779],"Chiseled Red Sandstone":[2579034],"Chiseled Sandstone":[2579291],"Chiseled Stone Bricks":[2579548],"Chlorine":[2599337],"Chorus Flower":[2732976,2732977,2732978,2732979,2732982,2732983],"Chorus Plant":[2733236],"Chromium":[2599594],"Clay Block":[2579805],"Coal Block":[2580062],"Coal Ore":[2580319],"Cobalt":[2599851],"Cobbled Deepslate":[2707793],"Cobbled Deepslate Slab":[2708048,2708050,2708051],"Cobbled Deepslate Stairs":[2708304,2708305,2708306,2708307,2708308,2708309,2708310,2708311],"Cobbled Deepslate Wall":[2708484,2708496,2708497,2708498,2708499,2708500,2708501,2708502,2708503,2708504,2708505,2708506,2708507,2708508,2708509,2708510,2708511,2708544,2708545,2708546,2708547,2708548,2708549,2708550,2708551,2708552,2708553,2708554,2708555,2708556,2708557,2708558,2708559,2708560,2708561,2708562,2708563,2708564,2708565,2708566,2708567,2708568,2708569,2708570,2708571,2708572,2708573,2708574,2708575,2708576,2708577,2708578,2708579,2708580,2708581,2708582,2708583,2708584,2708585,2708586,2708587,2708588,2708589,2708590,2708591,2708592,2708593,2708594,2708595,2708596,2708597,2708598,2708599,2708600,2708601,2708602,2708603,2708604,2708605,2708606,2708607,2708612,2708624,2708625,2708626,2708627,2708628,2708629,2708630,2708631,2708632,2708633,2708634,2708635,2708636,2708637,2708638,2708639,2708672,2708673,2708674,2708675,2708676,2708677,2708678,2708679,2708680,2708681,2708682,2708683,2708684,2708685,2708686,2708687,2708688,2708689,2708690,2708691,2708692,2708693,2708694,2708695,2708696,2708697,2708698,2708699,2708700,2708701,2708702,2708703,2708704,2708705,2708706,2708707,2708708,2708709,2708710,2708711,2708712,2708713,2708714,2708715,2708716,2708717,2708718,2708719,2708720,2708721,2708722,2708723,2708724,2708725,2708726,2708727,2708728,2708729,2708730,2708731,2708732,2708733,2708734,2708735],"Cobblestone":[2580576],"Cobblestone Slab":[2580832,2580833,2580835],"Cobblestone Stairs":[2581088,2581089,2581090,2581091,2581092,2581093,2581094,2581095],"Cobblestone Wall":[2581280,2581281,2581282,2581283,2581284,2581285,2581286,2581287,2581288,2581289,2581290,2581291,2581292,2581293,2581294,2581295,2581299,2581312,2581313,2581314,2581315,2581316,2581317,2581318,2581319,2581320,2581321,2581322,2581323,2581324,2581325,2581326,2581327,2581328,2581329,2581330,2581331,2581332,2581333,2581334,2581335,2581336,2581337,2581338,2581339,2581340,2581341,2581342,2581343,2581344,2581345,2581346,2581347,2581348,2581349,2581350,2581351,2581352,2581353,2581354,2581355,2581356,2581357,2581358,2581359,2581360,2581361,2581362,2581363,2581364,2581365,2581366,2581367,2581368,2581369,2581370,2581371,2581372,2581373,2581374,2581375,2581408,2581409,2581410,2581411,2581412,2581413,2581414,2581415,2581416,2581417,2581418,2581419,2581420,2581421,2581422,2581423,2581427,2581440,2581441,2581442,2581443,2581444,2581445,2581446,2581447,2581448,2581449,2581450,2581451,2581452,2581453,2581454,2581455,2581456,2581457,2581458,2581459,2581460,2581461,2581462,2581463,2581464,2581465,2581466,2581467,2581468,2581469,2581470,2581471,2581472,2581473,2581474,2581475,2581476,2581477,2581478,2581479,2581480,2581481,2581482,2581483,2581484,2581485,2581486,2581487,2581488,2581489,2581490,2581491,2581492,2581493,2581494,2581495,2581496,2581497,2581498,2581499,2581500,2581501,2581502,2581503],"Cobweb":[2581604],"Cocoa Block":[2581856,2581857,2581858,2581859,2581860,2581861,2581862,2581863,2581868,2581869,2581870,2581871],"Compound Creator":[2582116,2582117,2582118,2582119],"Concrete":[2582368,2582369,2582370,2582371,2582372,2582373,2582374,2582375,2582376,2582377,2582378,2582379,2582380,2582381,2582382,2582383],"Concrete Powder":[2582624,2582625,2582626,2582627,2582628,2582629,2582630,2582631,2582632,2582633,2582634,2582635,2582636,2582637,2582638,2582639],"Copernicium":[2600365],"Copper":[2600622],"Copper Block":[2728096,2728097,2728098,2728099,2728100,2728101,2728102,2728103],"Copper Ore":[2725012],"Coral":[2582880,2582881,2582882,2582883,2582885,2582888,2582889,2582890,2582891,2582893],"Coral Block":[2583136,2583137,2583138,2583139,2583142,2583144,2583145,2583146,2583147,2583150],"Coral Fan":[2583392,2583393,2583394,2583395,2583399,2583400,2583401,2583402,2583403,2583407,2583408,2583409,2583410,2583411,2583415,2583416,2583417,2583418,2583419,2583423],"Cornflower":[2583660],"Cracked Deepslate Bricks":[2706251],"Cracked Deepslate Tiles":[2707536],"Cracked Nether Bricks":[2710620],"Cracked Polished Blackstone Bricks":[2703424],"Cracked Stone Bricks":[2583917],"Crafting Table":[2584174],"Crimson Button":[2717298,2717299,2717300,2717301,2717302,2717303,2717306,2717307,2717308,2717309,2717310,2717311],"Crimson Door":[2718816,2718817,2718818,2718819,2718820,2718821,2718822,2718823,2718824,2718825,2718826,2718827,2718828,2718829,2718830,2718831,2718832,2718833,2718834,2718835,2718836,2718837,2718838,2718839,2718840,2718841,2718842,2718843,2718844,2718845,2718846,2718847],"Crimson Fence":[2713447],"Crimson Fence Gate":[2719600,2719601,2719602,2719603,2719604,2719605,2719606,2719607,2719608,2719609,2719610,2719611,2719612,2719613,2719614,2719615],"Crimson Hyphae":[2715760,2715761,2715762,2715763,2715764,2715765],"Crimson Planks":[2712676],"Crimson Pressure Plate":[2718072,2718073],"Crimson Sign":[2721152,2721153,2721154,2721155,2721156,2721157,2721158,2721159,2721160,2721161,2721162,2721163,2721164,2721165,2721166,2721167],"Crimson Slab":[2714216,2714218,2714219],"Crimson Stairs":[2720384,2720385,2720386,2720387,2720388,2720389,2720390,2720391],"Crimson Stem":[2714984,2714985,2714988,2714989,2714990,2714991],"Crimson Trapdoor":[2716528,2716529,2716530,2716531,2716532,2716533,2716534,2716535,2716536,2716537,2716538,2716539,2716540,2716541,2716542,2716543],"Crimson Wall Sign":[2721928,2721929,2721930,2721931],"Crying Obsidian":[2727325],"Curium":[2600879],"Cut Copper Block":[2728352,2728353,2728354,2728355,2728356,2728357,2728358,2728359],"Cut Copper Slab Slab":[2728608,2728609,2728610,2728611,2728612,2728613,2728614,2728615,2728616,2728617,2728618,2728619,2728620,2728621,2728622,2728623,2728624,2728625,2728626,2728627,2728628,2728629,2728630,2728631],"Cut Copper Stairs":[2728832,2728833,2728834,2728835,2728836,2728837,2728838,2728839,2728840,2728841,2728842,2728843,2728844,2728845,2728846,2728847,2728848,2728849,2728850,2728851,2728852,2728853,2728854,2728855,2728856,2728857,2728858,2728859,2728860,2728861,2728862,2728863,2728864,2728865,2728866,2728867,2728868,2728869,2728870,2728871,2728872,2728873,2728874,2728875,2728876,2728877,2728878,2728879,2728880,2728881,2728882,2728883,2728884,2728885,2728886,2728887,2728888,2728889,2728890,2728891,2728892,2728893,2728894,2728895],"Cut Red Sandstone":[2584431],"Cut Red Sandstone Slab":[2584688,2584689,2584690],"Cut Sandstone":[2584945],"Cut Sandstone Slab":[2585200,2585202,2585203],"Dandelion":[2585716],"Dark Oak Button":[2585968,2585969,2585972,2585973,2585974,2585975,2585976,2585977,2585980,2585981,2585982,2585983],"Dark Oak Door":[2586208,2586209,2586210,2586211,2586212,2586213,2586214,2586215,2586216,2586217,2586218,2586219,2586220,2586221,2586222,2586223,2586224,2586225,2586226,2586227,2586228,2586229,2586230,2586231,2586232,2586233,2586234,2586235,2586236,2586237,2586238,2586239],"Dark Oak Fence":[2586487],"Dark Oak Fence Gate":[2586736,2586737,2586738,2586739,2586740,2586741,2586742,2586743,2586744,2586745,2586746,2586747,2586748,2586749,2586750,2586751],"Dark Oak Leaves":[2587000,2587001,2587002,2587003],"Dark Oak Log":[2587256,2587257,2587258,2587259,2587262,2587263],"Dark Oak Planks":[2587515],"Dark Oak Pressure Plate":[2587772,2587773],"Dark Oak Sapling":[2588028,2588029],"Dark Oak Sign":[2588272,2588273,2588274,2588275,2588276,2588277,2588278,2588279,2588280,2588281,2588282,2588283,2588284,2588285,2588286,2588287],"Dark Oak Slab":[2588541,2588542,2588543],"Dark Oak Stairs":[2588800,2588801,2588802,2588803,2588804,2588805,2588806,2588807],"Dark Oak Trapdoor":[2589056,2589057,2589058,2589059,2589060,2589061,2589062,2589063,2589064,2589065,2589066,2589067,2589068,2589069,2589070,2589071],"Dark Oak Wall Sign":[2589312,2589313,2589314,2589315],"Dark Oak Wood":[2589568,2589569,2589570,2589571,2589574,2589575],"Dark Prismarine":[2589828],"Dark Prismarine Slab":[2590084,2590085,2590087],"Dark Prismarine Stairs":[2590336,2590337,2590338,2590339,2590340,2590341,2590342,2590343],"Darmstadtium":[2601136],"Daylight Sensor":[2590592,2590593,2590594,2590595,2590596,2590597,2590598,2590599,2590600,2590601,2590602,2590603,2590604,2590605,2590606,2590607,2590608,2590609,2590610,2590611,2590612,2590613,2590614,2590615,2590616,2590617,2590618,2590619,2590620,2590621,2590622,2590623],"Dead Bush":[2590856],"Deepslate":[2704964,2704966,2704967],"Deepslate Brick Slab":[2705480,2705481,2705482],"Deepslate Brick Stairs":[2705736,2705737,2705738,2705739,2705740,2705741,2705742,2705743],"Deepslate Brick Wall":[2705920,2705921,2705922,2705923,2705924,2705925,2705926,2705927,2705928,2705929,2705930,2705931,2705932,2705933,2705934,2705935,2705946,2705984,2705985,2705986,2705987,2705988,2705989,2705990,2705991,2705992,2705993,2705994,2705995,2705996,2705997,2705998,2705999,2706000,2706001,2706002,2706003,2706004,2706005,2706006,2706007,2706008,2706009,2706010,2706011,2706012,2706013,2706014,2706015,2706016,2706017,2706018,2706019,2706020,2706021,2706022,2706023,2706024,2706025,2706026,2706027,2706028,2706029,2706030,2706031,2706032,2706033,2706034,2706035,2706036,2706037,2706038,2706039,2706040,2706041,2706042,2706043,2706044,2706045,2706046,2706047,2706048,2706049,2706050,2706051,2706052,2706053,2706054,2706055,2706056,2706057,2706058,2706059,2706060,2706061,2706062,2706063,2706074,2706112,2706113,2706114,2706115,2706116,2706117,2706118,2706119,2706120,2706121,2706122,2706123,2706124,2706125,2706126,2706127,2706128,2706129,2706130,2706131,2706132,2706133,2706134,2706135,2706136,2706137,2706138,2706139,2706140,2706141,2706142,2706143,2706144,2706145,2706146,2706147,2706148,2706149,2706150,2706151,2706152,2706153,2706154,2706155,2706156,2706157,2706158,2706159,2706160,2706161,2706162,2706163,2706164,2706165,2706166,2706167,2706168,2706169,2706170,2706171,2706172,2706173,2706174,2706175],"Deepslate Bricks":[2705223],"Deepslate Coal Ore":[2722956],"Deepslate Copper Ore":[2724755],"Deepslate Diamond Ore":[2723213],"Deepslate Emerald Ore":[2723470],"Deepslate Gold Ore":[2724498],"Deepslate Iron Ore":[2724241],"Deepslate Lapis Lazuli Ore":[2723727],"Deepslate Redstone Ore":[2723984,2723985],"Deepslate Tile Slab":[2706764,2706765,2706767],"Deepslate Tile Stairs":[2707016,2707017,2707018,2707019,2707020,2707021,2707022,2707023],"Deepslate Tile Wall":[2707200,2707201,2707202,2707203,2707204,2707205,2707206,2707207,2707208,2707209,2707210,2707211,2707212,2707213,2707214,2707215,2707231,2707264,2707265,2707266,2707267,2707268,2707269,2707270,2707271,2707272,2707273,2707274,2707275,2707276,2707277,2707278,2707279,2707280,2707281,2707282,2707283,2707284,2707285,2707286,2707287,2707288,2707289,2707290,2707291,2707292,2707293,2707294,2707295,2707296,2707297,2707298,2707299,2707300,2707301,2707302,2707303,2707304,2707305,2707306,2707307,2707308,2707309,2707310,2707311,2707312,2707313,2707314,2707315,2707316,2707317,2707318,2707319,2707320,2707321,2707322,2707323,2707324,2707325,2707326,2707327,2707328,2707329,2707330,2707331,2707332,2707333,2707334,2707335,2707336,2707337,2707338,2707339,2707340,2707341,2707342,2707343,2707359,2707392,2707393,2707394,2707395,2707396,2707397,2707398,2707399,2707400,2707401,2707402,2707403,2707404,2707405,2707406,2707407,2707408,2707409,2707410,2707411,2707412,2707413,2707414,2707415,2707416,2707417,2707418,2707419,2707420,2707421,2707422,2707423,2707424,2707425,2707426,2707427,2707428,2707429,2707430,2707431,2707432,2707433,2707434,2707435,2707436,2707437,2707438,2707439,2707440,2707441,2707442,2707443,2707444,2707445,2707446,2707447,2707448,2707449,2707450,2707451,2707452,2707453,2707454,2707455],"Deepslate Tiles":[2706508],"Detector Rail":[2591104,2591105,2591106,2591107,2591108,2591109,2591112,2591113,2591114,2591115,2591116,2591117],"Diamond Block":[2591370],"Diamond Ore":[2591627],"Diorite":[2591884],"Diorite Slab":[2592140,2592141,2592143],"Diorite Stairs":[2592392,2592393,2592394,2592395,2592396,2592397,2592398,2592399],"Diorite Wall":[2592512,2592513,2592514,2592515,2592516,2592517,2592518,2592519,2592520,2592521,2592522,2592523,2592524,2592525,2592526,2592527,2592528,2592529,2592530,2592531,2592532,2592533,2592534,2592535,2592536,2592537,2592538,2592539,2592540,2592541,2592542,2592543,2592544,2592545,2592546,2592547,2592548,2592549,2592550,2592551,2592552,2592553,2592554,2592555,2592556,2592557,2592558,2592559,2592560,2592561,2592562,2592563,2592564,2592565,2592566,2592567,2592568,2592569,2592570,2592571,2592572,2592573,2592574,2592575,2592576,2592577,2592578,2592579,2592580,2592581,2592582,2592583,2592584,2592585,2592586,2592587,2592588,2592589,2592590,2592591,2592607,2592640,2592641,2592642,2592643,2592644,2592645,2592646,2592647,2592648,2592649,2592650,2592651,2592652,2592653,2592654,2592655,2592656,2592657,2592658,2592659,2592660,2592661,2592662,2592663,2592664,2592665,2592666,2592667,2592668,2592669,2592670,2592671,2592672,2592673,2592674,2592675,2592676,2592677,2592678,2592679,2592680,2592681,2592682,2592683,2592684,2592685,2592686,2592687,2592688,2592689,2592690,2592691,2592692,2592693,2592694,2592695,2592696,2592697,2592698,2592699,2592700,2592701,2592702,2592703,2592704,2592705,2592706,2592707,2592708,2592709,2592710,2592711,2592712,2592713,2592714,2592715,2592716,2592717,2592718,2592719,2592735],"Dirt":[2592912,2592913,2592914],"Double Tallgrass":[2593168,2593169],"Dragon Egg":[2593426],"Dried Kelp Block":[2593683],"Dubnium":[2601393],"Dyed Candle":[2729344,2729345,2729346,2729347,2729348,2729349,2729350,2729351,2729352,2729353,2729354,2729355,2729356,2729357,2729358,2729359,2729360,2729361,2729362,2729363,2729364,2729365,2729366,2729367,2729368,2729369,2729370,2729371,2729372,2729373,2729374,2729375,2729376,2729377,2729378,2729379,2729380,2729381,2729382,2729383,2729384,2729385,2729386,2729387,2729388,2729389,2729390,2729391,2729392,2729393,2729394,2729395,2729396,2729397,2729398,2729399,2729400,2729401,2729402,2729403,2729404,2729405,2729406,2729407,2729408,2729409,2729410,2729411,2729412,2729413,2729414,2729415,2729416,2729417,2729418,2729419,2729420,2729421,2729422,2729423,2729424,2729425,2729426,2729427,2729428,2729429,2729430,2729431,2729432,2729433,2729434,2729435,2729436,2729437,2729438,2729439,2729440,2729441,2729442,2729443,2729444,2729445,2729446,2729447,2729448,2729449,2729450,2729451,2729452,2729453,2729454,2729455,2729456,2729457,2729458,2729459,2729460,2729461,2729462,2729463,2729464,2729465,2729466,2729467,2729468,2729469,2729470,2729471],"Dyed Shulker Box":[2593936,2593937,2593938,2593939,2593940,2593941,2593942,2593943,2593944,2593945,2593946,2593947,2593948,2593949,2593950,2593951],"Dysprosium":[2601650],"Einsteinium":[2601907],"Element Constructor":[2600108,2600109,2600110,2600111],"Emerald Block":[2624781],"Emerald Ore":[2625038],"Enchanting Table":[2625295],"End Portal Frame":[2625552,2625553,2625554,2625555,2625556,2625557,2625558,2625559],"End Rod":[2625808,2625809,2625810,2625811,2625812,2625813],"End Stone":[2626066],"End Stone Brick Slab":[2626321,2626322,2626323],"End Stone Brick Stairs":[2626576,2626577,2626578,2626579,2626580,2626581,2626582,2626583],"End Stone Brick Wall":[2626816,2626817,2626818,2626819,2626820,2626821,2626822,2626823,2626824,2626825,2626826,2626827,2626828,2626829,2626830,2626831,2626832,2626833,2626834,2626835,2626836,2626837,2626838,2626839,2626840,2626841,2626842,2626843,2626844,2626845,2626846,2626847,2626848,2626849,2626850,2626851,2626852,2626853,2626854,2626855,2626856,2626857,2626858,2626859,2626860,2626861,2626862,2626863,2626864,2626865,2626866,2626867,2626868,2626869,2626870,2626871,2626872,2626873,2626874,2626875,2626876,2626877,2626878,2626879,2626885,2626896,2626897,2626898,2626899,2626900,2626901,2626902,2626903,2626904,2626905,2626906,2626907,2626908,2626909,2626910,2626911,2626944,2626945,2626946,2626947,2626948,2626949,2626950,2626951,2626952,2626953,2626954,2626955,2626956,2626957,2626958,2626959,2626960,2626961,2626962,2626963,2626964,2626965,2626966,2626967,2626968,2626969,2626970,2626971,2626972,2626973,2626974,2626975,2626976,2626977,2626978,2626979,2626980,2626981,2626982,2626983,2626984,2626985,2626986,2626987,2626988,2626989,2626990,2626991,2626992,2626993,2626994,2626995,2626996,2626997,2626998,2626999,2627000,2627001,2627002,2627003,2627004,2627005,2627006,2627007,2627013,2627024,2627025,2627026,2627027,2627028,2627029,2627030,2627031,2627032,2627033,2627034,2627035,2627036,2627037,2627038,2627039],"End Stone Bricks":[2627094],"Ender Chest":[2627348,2627349,2627350,2627351],"Erbium":[2602164],"Europium":[2602421],"Fake Wooden Slab":[2627608,2627609,2627610],"Farmland":[2627864,2627865,2627866,2627867,2627868,2627869,2627870,2627871],"Fermium":[2602678],"Fern":[2628122],"Fire Block":[2628368,2628369,2628370,2628371,2628372,2628373,2628374,2628375,2628376,2628377,2628378,2628379,2628380,2628381,2628382,2628383],"Flerovium":[2602935],"Fletching Table":[2628636],"Flower Pot":[2628893],"Flowering Azalea Leaves":[2736060,2736061,2736062,2736063],"Fluorine":[2603192],"Francium":[2603449],"Froglight":[2734001,2734002,2734003,2734005,2734006,2734007,2734013,2734014,2734015],"Frosted Ice":[2629148,2629149,2629150,2629151],"Furnace":[2629400,2629401,2629402,2629403,2629404,2629405,2629406,2629407],"Gadolinium":[2603706],"Gallium":[2603963],"Germanium":[2604220],"Gilded Blackstone":[2727582],"Glass":[2629664],"Glass Pane":[2629921],"Glazed Terracotta":[2697984,2697985,2697986,2697987,2697988,2697989,2697990,2697991,2697992,2697993,2697994,2697995,2697996,2697997,2697998,2697999,2698000,2698001,2698002,2698003,2698004,2698005,2698006,2698007,2698008,2698009,2698010,2698011,2698012,2698013,2698014,2698015,2698016,2698017,2698018,2698019,2698020,2698021,2698022,2698023,2698024,2698025,2698026,2698027,2698028,2698029,2698030,2698031,2698032,2698033,2698034,2698035,2698036,2698037,2698038,2698039,2698040,2698041,2698042,2698043,2698044,2698045,2698046,2698047],"Glow Item Frame":[2735280,2735281,2735284,2735285,2735286,2735287,2735288,2735289,2735292,2735293,2735294,2735295],"Glow Lichen":[2736832,2736833,2736834,2736835,2736836,2736837,2736838,2736839,2736840,2736841,2736842,2736843,2736844,2736845,2736846,2736847,2736848,2736849,2736850,2736851,2736852,2736853,2736854,2736855,2736856,2736857,2736858,2736859,2736860,2736861,2736862,2736863,2736864,2736865,2736866,2736867,2736868,2736869,2736870,2736871,2736872,2736873,2736874,2736875,2736876,2736877,2736878,2736879,2736880,2736881,2736882,2736883,2736884,2736885,2736886,2736887,2736888,2736889,2736890,2736891,2736892,2736893,2736894,2736895],"Glowing Obsidian":[2630178],"Glowstone":[2630435],"Gold":[2604477],"Gold Block":[2630692],"Gold Ore":[2630949],"Granite":[2631206],"Granite Slab":[2631461,2631462,2631463],"Granite Stairs":[2631720,2631721,2631722,2631723,2631724,2631725,2631726,2631727],"Granite Wall":[2631936,2631937,2631938,2631939,2631940,2631941,2631942,2631943,2631944,2631945,2631946,2631947,2631948,2631949,2631950,2631951,2631952,2631953,2631954,2631955,2631956,2631957,2631958,2631959,2631960,2631961,2631962,2631963,2631964,2631965,2631966,2631967,2631968,2631969,2631970,2631971,2631972,2631973,2631974,2631975,2631976,2631977,2631978,2631979,2631980,2631981,2631982,2631983,2631984,2631985,2631986,2631987,2631988,2631989,2631990,2631991,2631992,2631993,2631994,2631995,2631996,2631997,2631998,2631999,2632032,2632033,2632034,2632035,2632036,2632037,2632038,2632039,2632040,2632041,2632042,2632043,2632044,2632045,2632046,2632047,2632057,2632064,2632065,2632066,2632067,2632068,2632069,2632070,2632071,2632072,2632073,2632074,2632075,2632076,2632077,2632078,2632079,2632080,2632081,2632082,2632083,2632084,2632085,2632086,2632087,2632088,2632089,2632090,2632091,2632092,2632093,2632094,2632095,2632096,2632097,2632098,2632099,2632100,2632101,2632102,2632103,2632104,2632105,2632106,2632107,2632108,2632109,2632110,2632111,2632112,2632113,2632114,2632115,2632116,2632117,2632118,2632119,2632120,2632121,2632122,2632123,2632124,2632125,2632126,2632127,2632160,2632161,2632162,2632163,2632164,2632165,2632166,2632167,2632168,2632169,2632170,2632171,2632172,2632173,2632174,2632175,2632185],"Grass":[2632234],"Grass Path":[2632491],"Gravel":[2632748],"Green Torch":[2633514,2633515,2633516,2633517,2633518],"Hafnium":[2604734],"Hanging Roots":[2730409],"Hardened Clay":[2633776],"Hardened Glass":[2634033],"Hardened Glass Pane":[2634290],"Hassium":[2604991],"Hay Bale":[2634545,2634546,2634547],"Heat Block":[2578263],"Helium":[2605248],"Holmium":[2605505],"Honeycomb Block":[2722699],"Hopper":[2634800,2634801,2634804,2634806,2634807,2634808,2634809,2634812,2634814,2634815],"Hydrogen":[2605762],"Ice":[2635061],"Indium":[2606019],"Infested Chiseled Stone Brick":[2635318],"Infested Cobblestone":[2635575],"Infested Cracked Stone Brick":[2635832],"Infested Mossy Stone Brick":[2636089],"Infested Stone":[2636346],"Infested Stone Brick":[2636603],"Invisible Bedrock":[2637374],"Iodine":[2606276],"Iridium":[2606533],"Iron":[2606790],"Iron Bars":[2637888],"Iron Block":[2637631],"Iron Door":[2638144,2638145,2638146,2638147,2638148,2638149,2638150,2638151,2638152,2638153,2638154,2638155,2638156,2638157,2638158,2638159,2638160,2638161,2638162,2638163,2638164,2638165,2638166,2638167,2638168,2638169,2638170,2638171,2638172,2638173,2638174,2638175],"Iron Ore":[2638402],"Iron Trapdoor":[2638656,2638657,2638658,2638659,2638660,2638661,2638662,2638663,2638664,2638665,2638666,2638667,2638668,2638669,2638670,2638671],"Item Frame":[2638912,2638913,2638916,2638917,2638918,2638919,2638920,2638921,2638924,2638925,2638926,2638927],"Jack o'Lantern":[2647396,2647397,2647398,2647399],"Jukebox":[2639173],"Jungle Button":[2639426,2639427,2639428,2639429,2639430,2639431,2639434,2639435,2639436,2639437,2639438,2639439],"Jungle Door":[2639680,2639681,2639682,2639683,2639684,2639685,2639686,2639687,2639688,2639689,2639690,2639691,2639692,2639693,2639694,2639695,2639696,2639697,2639698,2639699,2639700,2639701,2639702,2639703,2639704,2639705,2639706,2639707,2639708,2639709,2639710,2639711],"Jungle Fence":[2639944],"Jungle Fence Gate":[2640192,2640193,2640194,2640195,2640196,2640197,2640198,2640199,2640200,2640201,2640202,2640203,2640204,2640205,2640206,2640207],"Jungle Leaves":[2640456,2640457,2640458,2640459],"Jungle Log":[2640712,2640713,2640714,2640715,2640718,2640719],"Jungle Planks":[2640972],"Jungle Pressure Plate":[2641228,2641229],"Jungle Sapling":[2641486,2641487],"Jungle Sign":[2641728,2641729,2641730,2641731,2641732,2641733,2641734,2641735,2641736,2641737,2641738,2641739,2641740,2641741,2641742,2641743],"Jungle Slab":[2642000,2642001,2642002],"Jungle Stairs":[2642256,2642257,2642258,2642259,2642260,2642261,2642262,2642263],"Jungle Trapdoor":[2642512,2642513,2642514,2642515,2642516,2642517,2642518,2642519,2642520,2642521,2642522,2642523,2642524,2642525,2642526,2642527],"Jungle Wall Sign":[2642768,2642769,2642770,2642771],"Jungle Wood":[2643024,2643025,2643028,2643029,2643030,2643031],"Krypton":[2607047],"Lab Table":[2643284,2643285,2643286,2643287],"Ladder":[2643540,2643541,2643542,2643543],"Lantern":[2643798,2643799],"Lanthanum":[2607304],"Lapis Lazuli Block":[2644056],"Lapis Lazuli Ore":[2644313],"Large Fern":[2644570,2644571],"Lava":[2644800,2644801,2644802,2644803,2644804,2644805,2644806,2644807,2644808,2644809,2644810,2644811,2644812,2644813,2644814,2644815,2644816,2644817,2644818,2644819,2644820,2644821,2644822,2644823,2644824,2644825,2644826,2644827,2644828,2644829,2644830,2644831],"Lava Cauldron":[2732208,2732209,2732210,2732211,2732212,2732213],"Lawrencium":[2607561],"Lead":[2607818],"Lectern":[2645080,2645081,2645082,2645083,2645084,2645085,2645086,2645087],"Legacy Stonecutter":[2645341],"Lever":[2645584,2645585,2645586,2645587,2645588,2645589,2645590,2645591,2645592,2645593,2645594,2645595,2645596,2645597,2645598,2645599],"Light Block":[2703680,2703681,2703682,2703683,2703684,2703685,2703686,2703687,2703688,2703689,2703690,2703691,2703692,2703693,2703694,2703695],"Lightning Rod":[2727834,2727835,2727836,2727837,2727838,2727839],"Lilac":[2646368,2646369],"Lily Pad":[2646883],"Lily of the Valley":[2646626],"Lithium":[2608075],"Livermorium":[2608332],"Loom":[2647652,2647653,2647654,2647655],"Lutetium":[2608589],"Magma Block":[2648168],"Magnesium":[2608846],"Manganese":[2609103],"Mangrove Button":[2717040,2717041,2717044,2717045,2717046,2717047,2717048,2717049,2717052,2717053,2717054,2717055],"Mangrove Door":[2718560,2718561,2718562,2718563,2718564,2718565,2718566,2718567,2718568,2718569,2718570,2718571,2718572,2718573,2718574,2718575,2718576,2718577,2718578,2718579,2718580,2718581,2718582,2718583,2718584,2718585,2718586,2718587,2718588,2718589,2718590,2718591],"Mangrove Fence":[2713190],"Mangrove Fence Gate":[2719344,2719345,2719346,2719347,2719348,2719349,2719350,2719351,2719352,2719353,2719354,2719355,2719356,2719357,2719358,2719359],"Mangrove Leaves":[2735548,2735549,2735550,2735551],"Mangrove Log":[2714728,2714729,2714732,2714733,2714734,2714735],"Mangrove Planks":[2712419],"Mangrove Pressure Plate":[2717816,2717817],"Mangrove Roots":[2733493],"Mangrove Sign":[2720896,2720897,2720898,2720899,2720900,2720901,2720902,2720903,2720904,2720905,2720906,2720907,2720908,2720909,2720910,2720911],"Mangrove Slab":[2713960,2713961,2713963],"Mangrove Stairs":[2720128,2720129,2720130,2720131,2720132,2720133,2720134,2720135],"Mangrove Trapdoor":[2716272,2716273,2716274,2716275,2716276,2716277,2716278,2716279,2716280,2716281,2716282,2716283,2716284,2716285,2716286,2716287],"Mangrove Wall Sign":[2721668,2721669,2721670,2721671],"Mangrove Wood":[2715498,2715499,2715500,2715501,2715502,2715503],"Material Reducer":[2648424,2648425,2648426,2648427],"Meitnerium":[2609360],"Melon Block":[2648682],"Melon Stem":[2648896,2648897,2648898,2648899,2648900,2648901,2648902,2648903,2648904,2648905,2648906,2648907,2648908,2648909,2648910,2648911,2648928,2648929,2648930,2648931,2648932,2648933,2648934,2648935,2648944,2648945,2648946,2648947,2648948,2648949,2648950,2648951,2648952,2648953,2648954,2648955,2648956,2648957,2648958,2648959],"Mendelevium":[2609617],"Mercury":[2609874],"Mob Head":[2649152,2649153,2649154,2649156,2649157,2649158,2649159,2649160,2649161,2649162,2649164,2649165,2649166,2649167,2649184,2649185,2649186,2649188,2649189,2649190,2649191,2649200,2649201,2649202,2649204,2649205,2649206,2649207,2649208,2649209,2649210,2649212,2649213,2649214,2649215],"Molybdenum":[2610131],"Monster Spawner":[2649453],"Moscovium":[2610388],"Mossy Cobblestone":[2649710],"Mossy Cobblestone Slab":[2649965,2649966,2649967],"Mossy Cobblestone Stairs":[2650224,2650225,2650226,2650227,2650228,2650229,2650230,2650231],"Mossy Cobblestone Wall":[2650401,2650416,2650417,2650418,2650419,2650420,2650421,2650422,2650423,2650424,2650425,2650426,2650427,2650428,2650429,2650430,2650431,2650432,2650433,2650434,2650435,2650436,2650437,2650438,2650439,2650440,2650441,2650442,2650443,2650444,2650445,2650446,2650447,2650448,2650449,2650450,2650451,2650452,2650453,2650454,2650455,2650456,2650457,2650458,2650459,2650460,2650461,2650462,2650463,2650464,2650465,2650466,2650467,2650468,2650469,2650470,2650471,2650472,2650473,2650474,2650475,2650476,2650477,2650478,2650479,2650480,2650481,2650482,2650483,2650484,2650485,2650486,2650487,2650488,2650489,2650490,2650491,2650492,2650493,2650494,2650495,2650529,2650544,2650545,2650546,2650547,2650548,2650549,2650550,2650551,2650552,2650553,2650554,2650555,2650556,2650557,2650558,2650559,2650560,2650561,2650562,2650563,2650564,2650565,2650566,2650567,2650568,2650569,2650570,2650571,2650572,2650573,2650574,2650575,2650576,2650577,2650578,2650579,2650580,2650581,2650582,2650583,2650584,2650585,2650586,2650587,2650588,2650589,2650590,2650591,2650592,2650593,2650594,2650595,2650596,2650597,2650598,2650599,2650600,2650601,2650602,2650603,2650604,2650605,2650606,2650607,2650608,2650609,2650610,2650611,2650612,2650613,2650614,2650615,2650616,2650617,2650618,2650619,2650620,2650621,2650622,2650623],"Mossy Stone Brick Slab":[2650736,2650738,2650739],"Mossy Stone Brick Stairs":[2650992,2650993,2650994,2650995,2650996,2650997,2650998,2650999],"Mossy Stone Brick Wall":[2651172,2651184,2651185,2651186,2651187,2651188,2651189,2651190,2651191,2651192,2651193,2651194,2651195,2651196,2651197,2651198,2651199,2651200,2651201,2651202,2651203,2651204,2651205,2651206,2651207,2651208,2651209,2651210,2651211,2651212,2651213,2651214,2651215,2651216,2651217,2651218,2651219,2651220,2651221,2651222,2651223,2651224,2651225,2651226,2651227,2651228,2651229,2651230,2651231,2651232,2651233,2651234,2651235,2651236,2651237,2651238,2651239,2651240,2651241,2651242,2651243,2651244,2651245,2651246,2651247,2651248,2651249,2651250,2651251,2651252,2651253,2651254,2651255,2651256,2651257,2651258,2651259,2651260,2651261,2651262,2651263,2651300,2651312,2651313,2651314,2651315,2651316,2651317,2651318,2651319,2651320,2651321,2651322,2651323,2651324,2651325,2651326,2651327,2651328,2651329,2651330,2651331,2651332,2651333,2651334,2651335,2651336,2651337,2651338,2651339,2651340,2651341,2651342,2651343,2651344,2651345,2651346,2651347,2651348,2651349,2651350,2651351,2651352,2651353,2651354,2651355,2651356,2651357,2651358,2651359,2651360,2651361,2651362,2651363,2651364,2651365,2651366,2651367,2651368,2651369,2651370,2651371,2651372,2651373,2651374,2651375,2651376,2651377,2651378,2651379,2651380,2651381,2651382,2651383,2651384,2651385,2651386,2651387,2651388,2651389,2651390,2651391],"Mossy Stone Bricks":[2651509],"Mud":[2725526],"Mud Brick Slab":[2726040,2726041,2726042],"Mud Brick Stairs":[2726296,2726297,2726298,2726299,2726300,2726301,2726302,2726303],"Mud Brick Wall":[2726400,2726401,2726402,2726403,2726404,2726405,2726406,2726407,2726408,2726409,2726410,2726411,2726412,2726413,2726414,2726415,2726416,2726417,2726418,2726419,2726420,2726421,2726422,2726423,2726424,2726425,2726426,2726427,2726428,2726429,2726430,2726431,2726432,2726433,2726434,2726435,2726436,2726437,2726438,2726439,2726440,2726441,2726442,2726443,2726444,2726445,2726446,2726447,2726448,2726449,2726450,2726451,2726452,2726453,2726454,2726455,2726456,2726457,2726458,2726459,2726460,2726461,2726462,2726463,2726474,2726480,2726481,2726482,2726483,2726484,2726485,2726486,2726487,2726488,2726489,2726490,2726491,2726492,2726493,2726494,2726495,2726528,2726529,2726530,2726531,2726532,2726533,2726534,2726535,2726536,2726537,2726538,2726539,2726540,2726541,2726542,2726543,2726544,2726545,2726546,2726547,2726548,2726549,2726550,2726551,2726552,2726553,2726554,2726555,2726556,2726557,2726558,2726559,2726560,2726561,2726562,2726563,2726564,2726565,2726566,2726567,2726568,2726569,2726570,2726571,2726572,2726573,2726574,2726575,2726576,2726577,2726578,2726579,2726580,2726581,2726582,2726583,2726584,2726585,2726586,2726587,2726588,2726589,2726590,2726591,2726602,2726608,2726609,2726610,2726611,2726612,2726613,2726614,2726615,2726616,2726617,2726618,2726619,2726620,2726621,2726622,2726623],"Mud Bricks":[2725783],"Muddy Mangrove Roots":[2733748,2733750,2733751],"Mushroom Stem":[2651766],"Mycelium":[2652023],"Neodymium":[2610645],"Neon":[2610902],"Neptunium":[2611159],"Nether Brick Fence":[2652280],"Nether Brick Slab":[2652536,2652537,2652539],"Nether Brick Stairs":[2652792,2652793,2652794,2652795,2652796,2652797,2652798,2652799],"Nether Brick Wall":[2652971,2652976,2652977,2652978,2652979,2652980,2652981,2652982,2652983,2652984,2652985,2652986,2652987,2652988,2652989,2652990,2652991,2652992,2652993,2652994,2652995,2652996,2652997,2652998,2652999,2653000,2653001,2653002,2653003,2653004,2653005,2653006,2653007,2653008,2653009,2653010,2653011,2653012,2653013,2653014,2653015,2653016,2653017,2653018,2653019,2653020,2653021,2653022,2653023,2653024,2653025,2653026,2653027,2653028,2653029,2653030,2653031,2653032,2653033,2653034,2653035,2653036,2653037,2653038,2653039,2653040,2653041,2653042,2653043,2653044,2653045,2653046,2653047,2653048,2653049,2653050,2653051,2653052,2653053,2653054,2653055,2653099,2653104,2653105,2653106,2653107,2653108,2653109,2653110,2653111,2653112,2653113,2653114,2653115,2653116,2653117,2653118,2653119,2653120,2653121,2653122,2653123,2653124,2653125,2653126,2653127,2653128,2653129,2653130,2653131,2653132,2653133,2653134,2653135,2653136,2653137,2653138,2653139,2653140,2653141,2653142,2653143,2653144,2653145,2653146,2653147,2653148,2653149,2653150,2653151,2653152,2653153,2653154,2653155,2653156,2653157,2653158,2653159,2653160,2653161,2653162,2653163,2653164,2653165,2653166,2653167,2653168,2653169,2653170,2653171,2653172,2653173,2653174,2653175,2653176,2653177,2653178,2653179,2653180,2653181,2653182,2653183],"Nether Bricks":[2653308],"Nether Gold Ore":[2725269],"Nether Portal":[2653564,2653565],"Nether Quartz Ore":[2653822],"Nether Reactor Core":[2654079],"Nether Wart":[2654336,2654337,2654338,2654339],"Nether Wart Block":[2654593],"Netherite Block":[2731180],"Netherrack":[2654850],"Nickel":[2611416],"Nihonium":[2611673],"Niobium":[2611930],"Nitrogen":[2612187],"Nobelium":[2612444],"Note Block":[2655107],"Oak Button":[2655360,2655361,2655364,2655365,2655366,2655367,2655368,2655369,2655372,2655373,2655374,2655375],"Oak Door":[2655616,2655617,2655618,2655619,2655620,2655621,2655622,2655623,2655624,2655625,2655626,2655627,2655628,2655629,2655630,2655631,2655632,2655633,2655634,2655635,2655636,2655637,2655638,2655639,2655640,2655641,2655642,2655643,2655644,2655645,2655646,2655647],"Oak Fence":[2655878],"Oak Fence Gate":[2656128,2656129,2656130,2656131,2656132,2656133,2656134,2656135,2656136,2656137,2656138,2656139,2656140,2656141,2656142,2656143],"Oak Leaves":[2656392,2656393,2656394,2656395],"Oak Log":[2656648,2656649,2656650,2656651,2656652,2656653],"Oak Planks":[2656906],"Oak Pressure Plate":[2657162,2657163],"Oak Sapling":[2657420,2657421],"Oak Sign":[2657664,2657665,2657666,2657667,2657668,2657669,2657670,2657671,2657672,2657673,2657674,2657675,2657676,2657677,2657678,2657679],"Oak Slab":[2657932,2657934,2657935],"Oak Stairs":[2658184,2658185,2658186,2658187,2658188,2658189,2658190,2658191],"Oak Trapdoor":[2658448,2658449,2658450,2658451,2658452,2658453,2658454,2658455,2658456,2658457,2658458,2658459,2658460,2658461,2658462,2658463],"Oak Wall Sign":[2658704,2658705,2658706,2658707],"Oak Wood":[2658960,2658961,2658962,2658963,2658966,2658967],"Obsidian":[2659219],"Oganesson":[2612701],"Orange Tulip":[2659733],"Osmium":[2612958],"Oxeye Daisy":[2659990],"Oxygen":[2613215],"Packed Ice":[2660247],"Packed Mud":[2726811],"Palladium":[2613472],"Peony":[2660504,2660505],"Phosphorus":[2613729],"Pink Tulip":[2661018],"Platinum":[2613986],"Plutonium":[2614243],"Podzol":[2661275],"Polished Andesite":[2661532],"Polished Andesite Slab":[2661788,2661789,2661791],"Polished Andesite Stairs":[2662040,2662041,2662042,2662043,2662044,2662045,2662046,2662047],"Polished Basalt":[2699053,2699054,2699055],"Polished Blackstone":[2700597],"Polished Blackstone Brick Slab":[2702652,2702653,2702655],"Polished Blackstone Brick Stairs":[2702904,2702905,2702906,2702907,2702908,2702909,2702910,2702911],"Polished Blackstone Brick Wall":[2703104,2703105,2703106,2703107,2703108,2703109,2703110,2703111,2703112,2703113,2703114,2703115,2703116,2703117,2703118,2703119,2703120,2703121,2703122,2703123,2703124,2703125,2703126,2703127,2703128,2703129,2703130,2703131,2703132,2703133,2703134,2703135,2703136,2703137,2703138,2703139,2703140,2703141,2703142,2703143,2703144,2703145,2703146,2703147,2703148,2703149,2703150,2703151,2703152,2703153,2703154,2703155,2703156,2703157,2703158,2703159,2703160,2703161,2703162,2703163,2703164,2703165,2703166,2703167,2703215,2703216,2703217,2703218,2703219,2703220,2703221,2703222,2703223,2703224,2703225,2703226,2703227,2703228,2703229,2703230,2703231,2703232,2703233,2703234,2703235,2703236,2703237,2703238,2703239,2703240,2703241,2703242,2703243,2703244,2703245,2703246,2703247,2703248,2703249,2703250,2703251,2703252,2703253,2703254,2703255,2703256,2703257,2703258,2703259,2703260,2703261,2703262,2703263,2703264,2703265,2703266,2703267,2703268,2703269,2703270,2703271,2703272,2703273,2703274,2703275,2703276,2703277,2703278,2703279,2703280,2703281,2703282,2703283,2703284,2703285,2703286,2703287,2703288,2703289,2703290,2703291,2703292,2703293,2703294,2703295,2703343,2703344,2703345,2703346,2703347,2703348,2703349,2703350,2703351,2703352,2703353,2703354,2703355,2703356,2703357,2703358,2703359],"Polished Blackstone Bricks":[2702396],"Polished Blackstone Button":[2700850,2700851,2700852,2700853,2700854,2700855,2700858,2700859,2700860,2700861,2700862,2700863],"Polished Blackstone Pressure Plate":[2701110,2701111],"Polished Blackstone Slab":[2701368,2701369,2701370],"Polished Blackstone Stairs":[2701624,2701625,2701626,2701627,2701628,2701629,2701630,2701631],"Polished Blackstone Wall":[2701824,2701825,2701826,2701827,2701828,2701829,2701830,2701831,2701832,2701833,2701834,2701835,2701836,2701837,2701838,2701839,2701840,2701841,2701842,2701843,2701844,2701845,2701846,2701847,2701848,2701849,2701850,2701851,2701852,2701853,2701854,2701855,2701856,2701857,2701858,2701859,2701860,2701861,2701862,2701863,2701864,2701865,2701866,2701867,2701868,2701869,2701870,2701871,2701872,2701873,2701874,2701875,2701876,2701877,2701878,2701879,2701880,2701881,2701882,2701883,2701884,2701885,2701886,2701887,2701930,2701936,2701937,2701938,2701939,2701940,2701941,2701942,2701943,2701944,2701945,2701946,2701947,2701948,2701949,2701950,2701951,2701952,2701953,2701954,2701955,2701956,2701957,2701958,2701959,2701960,2701961,2701962,2701963,2701964,2701965,2701966,2701967,2701968,2701969,2701970,2701971,2701972,2701973,2701974,2701975,2701976,2701977,2701978,2701979,2701980,2701981,2701982,2701983,2701984,2701985,2701986,2701987,2701988,2701989,2701990,2701991,2701992,2701993,2701994,2701995,2701996,2701997,2701998,2701999,2702000,2702001,2702002,2702003,2702004,2702005,2702006,2702007,2702008,2702009,2702010,2702011,2702012,2702013,2702014,2702015,2702058,2702064,2702065,2702066,2702067,2702068,2702069,2702070,2702071,2702072,2702073,2702074,2702075,2702076,2702077,2702078,2702079],"Polished Deepslate":[2708821],"Polished Deepslate Slab":[2709076,2709078,2709079],"Polished Deepslate Stairs":[2709328,2709329,2709330,2709331,2709332,2709333,2709334,2709335],"Polished Deepslate Wall":[2709512,2709520,2709521,2709522,2709523,2709524,2709525,2709526,2709527,2709528,2709529,2709530,2709531,2709532,2709533,2709534,2709535,2709568,2709569,2709570,2709571,2709572,2709573,2709574,2709575,2709576,2709577,2709578,2709579,2709580,2709581,2709582,2709583,2709584,2709585,2709586,2709587,2709588,2709589,2709590,2709591,2709592,2709593,2709594,2709595,2709596,2709597,2709598,2709599,2709600,2709601,2709602,2709603,2709604,2709605,2709606,2709607,2709608,2709609,2709610,2709611,2709612,2709613,2709614,2709615,2709616,2709617,2709618,2709619,2709620,2709621,2709622,2709623,2709624,2709625,2709626,2709627,2709628,2709629,2709630,2709631,2709640,2709648,2709649,2709650,2709651,2709652,2709653,2709654,2709655,2709656,2709657,2709658,2709659,2709660,2709661,2709662,2709663,2709696,2709697,2709698,2709699,2709700,2709701,2709702,2709703,2709704,2709705,2709706,2709707,2709708,2709709,2709710,2709711,2709712,2709713,2709714,2709715,2709716,2709717,2709718,2709719,2709720,2709721,2709722,2709723,2709724,2709725,2709726,2709727,2709728,2709729,2709730,2709731,2709732,2709733,2709734,2709735,2709736,2709737,2709738,2709739,2709740,2709741,2709742,2709743,2709744,2709745,2709746,2709747,2709748,2709749,2709750,2709751,2709752,2709753,2709754,2709755,2709756,2709757,2709758,2709759],"Polished Diorite":[2662303],"Polished Diorite Slab":[2662560,2662561,2662562],"Polished Diorite Stairs":[2662816,2662817,2662818,2662819,2662820,2662821,2662822,2662823],"Polished Granite":[2663074],"Polished Granite Slab":[2663329,2663330,2663331],"Polished Granite Stairs":[2663584,2663585,2663586,2663587,2663588,2663589,2663590,2663591],"Polonium":[2614500],"Poppy":[2663845],"Potassium":[2614757],"Potato Block":[2664096,2664097,2664098,2664099,2664100,2664101,2664102,2664103],"Potion Cauldron":[2732464,2732465,2732466,2732467,2732468,2732469],"Powered Rail":[2664354,2664355,2664356,2664357,2664358,2664359,2664362,2664363,2664364,2664365,2664366,2664367],"Praseodymium":[2615014],"Prismarine":[2664616],"Prismarine Bricks":[2664873],"Prismarine Bricks Slab":[2665128,2665130,2665131],"Prismarine Bricks Stairs":[2665384,2665385,2665386,2665387,2665388,2665389,2665390,2665391],"Prismarine Slab":[2665644,2665645,2665646],"Prismarine Stairs":[2665896,2665897,2665898,2665899,2665900,2665901,2665902,2665903],"Prismarine Wall":[2665984,2665985,2665986,2665987,2665988,2665989,2665990,2665991,2665992,2665993,2665994,2665995,2665996,2665997,2665998,2665999,2666000,2666001,2666002,2666003,2666004,2666005,2666006,2666007,2666008,2666009,2666010,2666011,2666012,2666013,2666014,2666015,2666016,2666017,2666018,2666019,2666020,2666021,2666022,2666023,2666024,2666025,2666026,2666027,2666028,2666029,2666030,2666031,2666032,2666033,2666034,2666035,2666036,2666037,2666038,2666039,2666040,2666041,2666042,2666043,2666044,2666045,2666046,2666047,2666080,2666081,2666082,2666083,2666084,2666085,2666086,2666087,2666088,2666089,2666090,2666091,2666092,2666093,2666094,2666095,2666110,2666112,2666113,2666114,2666115,2666116,2666117,2666118,2666119,2666120,2666121,2666122,2666123,2666124,2666125,2666126,2666127,2666128,2666129,2666130,2666131,2666132,2666133,2666134,2666135,2666136,2666137,2666138,2666139,2666140,2666141,2666142,2666143,2666144,2666145,2666146,2666147,2666148,2666149,2666150,2666151,2666152,2666153,2666154,2666155,2666156,2666157,2666158,2666159,2666160,2666161,2666162,2666163,2666164,2666165,2666166,2666167,2666168,2666169,2666170,2666171,2666172,2666173,2666174,2666175,2666208,2666209,2666210,2666211,2666212,2666213,2666214,2666215,2666216,2666217,2666218,2666219,2666220,2666221,2666222,2666223,2666238],"Promethium":[2615271],"Protactinium":[2615528],"Pumpkin":[2666415],"Pumpkin Stem":[2666640,2666641,2666642,2666643,2666644,2666645,2666646,2666647,2666648,2666649,2666650,2666651,2666652,2666653,2666654,2666655,2666656,2666657,2666658,2666659,2666660,2666661,2666662,2666663,2666664,2666665,2666666,2666667,2666668,2666669,2666670,2666671,2666680,2666681,2666682,2666683,2666684,2666685,2666686,2666687],"Purple Torch":[2667184,2667185,2667187,2667190,2667191],"Purpur Block":[2667443],"Purpur Pillar":[2667700,2667701,2667702],"Purpur Slab":[2667956,2667957,2667959],"Purpur Stairs":[2668208,2668209,2668210,2668211,2668212,2668213,2668214,2668215],"Quartz Block":[2668471],"Quartz Bricks":[2709849],"Quartz Pillar":[2668728,2668729,2668730],"Quartz Slab":[2668984,2668985,2668987],"Quartz Stairs":[2669240,2669241,2669242,2669243,2669244,2669245,2669246,2669247],"Radium":[2615785],"Radon":[2616042],"Rail":[2669490,2669491,2669496,2669497,2669498,2669499,2669500,2669501,2669502,2669503],"Raw Copper Block":[2703938],"Raw Gold Block":[2704195],"Raw Iron Block":[2704452],"Red Mushroom":[2670013],"Red Mushroom Block":[2670260,2670262,2670263,2670264,2670265,2670266,2670267,2670268,2670269,2670270,2670271],"Red Nether Brick Slab":[2670525,2670526,2670527],"Red Nether Brick Stairs":[2670784,2670785,2670786,2670787,2670788,2670789,2670790,2670791],"Red Nether Brick Wall":[2670848,2670849,2670850,2670851,2670852,2670853,2670854,2670855,2670856,2670857,2670858,2670859,2670860,2670861,2670862,2670863,2670865,2670912,2670913,2670914,2670915,2670916,2670917,2670918,2670919,2670920,2670921,2670922,2670923,2670924,2670925,2670926,2670927,2670928,2670929,2670930,2670931,2670932,2670933,2670934,2670935,2670936,2670937,2670938,2670939,2670940,2670941,2670942,2670943,2670944,2670945,2670946,2670947,2670948,2670949,2670950,2670951,2670952,2670953,2670954,2670955,2670956,2670957,2670958,2670959,2670960,2670961,2670962,2670963,2670964,2670965,2670966,2670967,2670968,2670969,2670970,2670971,2670972,2670973,2670974,2670975,2670976,2670977,2670978,2670979,2670980,2670981,2670982,2670983,2670984,2670985,2670986,2670987,2670988,2670989,2670990,2670991,2670993,2671040,2671041,2671042,2671043,2671044,2671045,2671046,2671047,2671048,2671049,2671050,2671051,2671052,2671053,2671054,2671055,2671056,2671057,2671058,2671059,2671060,2671061,2671062,2671063,2671064,2671065,2671066,2671067,2671068,2671069,2671070,2671071,2671072,2671073,2671074,2671075,2671076,2671077,2671078,2671079,2671080,2671081,2671082,2671083,2671084,2671085,2671086,2671087,2671088,2671089,2671090,2671091,2671092,2671093,2671094,2671095,2671096,2671097,2671098,2671099,2671100,2671101,2671102,2671103],"Red Nether Bricks":[2671298],"Red Sand":[2671555],"Red Sandstone":[2671812],"Red Sandstone Slab":[2672068,2672069,2672071],"Red Sandstone Stairs":[2672320,2672321,2672322,2672323,2672324,2672325,2672326,2672327],"Red Sandstone Wall":[2672384,2672385,2672386,2672387,2672388,2672389,2672390,2672391,2672392,2672393,2672394,2672395,2672396,2672397,2672398,2672399,2672407,2672448,2672449,2672450,2672451,2672452,2672453,2672454,2672455,2672456,2672457,2672458,2672459,2672460,2672461,2672462,2672463,2672464,2672465,2672466,2672467,2672468,2672469,2672470,2672471,2672472,2672473,2672474,2672475,2672476,2672477,2672478,2672479,2672480,2672481,2672482,2672483,2672484,2672485,2672486,2672487,2672488,2672489,2672490,2672491,2672492,2672493,2672494,2672495,2672496,2672497,2672498,2672499,2672500,2672501,2672502,2672503,2672504,2672505,2672506,2672507,2672508,2672509,2672510,2672511,2672512,2672513,2672514,2672515,2672516,2672517,2672518,2672519,2672520,2672521,2672522,2672523,2672524,2672525,2672526,2672527,2672535,2672576,2672577,2672578,2672579,2672580,2672581,2672582,2672583,2672584,2672585,2672586,2672587,2672588,2672589,2672590,2672591,2672592,2672593,2672594,2672595,2672596,2672597,2672598,2672599,2672600,2672601,2672602,2672603,2672604,2672605,2672606,2672607,2672608,2672609,2672610,2672611,2672612,2672613,2672614,2672615,2672616,2672617,2672618,2672619,2672620,2672621,2672622,2672623,2672624,2672625,2672626,2672627,2672628,2672629,2672630,2672631,2672632,2672633,2672634,2672635,2672636,2672637,2672638,2672639],"Red Torch":[2672841,2672842,2672843,2672844,2672845],"Red Tulip":[2673097],"Redstone":[2674896,2674897,2674898,2674899,2674900,2674901,2674902,2674903,2674904,2674905,2674906,2674907,2674908,2674909,2674910,2674911],"Redstone Block":[2673354],"Redstone Comparator":[2673600,2673601,2673602,2673603,2673604,2673605,2673606,2673607,2673608,2673609,2673610,2673611,2673612,2673613,2673614,2673615],"Redstone Lamp":[2673868,2673869],"Redstone Ore":[2674124,2674125],"Redstone Repeater":[2674368,2674369,2674370,2674371,2674372,2674373,2674374,2674375,2674376,2674377,2674378,2674379,2674380,2674381,2674382,2674383,2674384,2674385,2674386,2674387,2674388,2674389,2674390,2674391,2674392,2674393,2674394,2674395,2674396,2674397,2674398,2674399],"Redstone Torch":[2674626,2674627,2674628,2674629,2674630,2674634,2674635,2674636,2674637,2674638],"Reinforced Deepslate":[2736320],"Rhenium":[2616299],"Rhodium":[2616556],"Roentgenium":[2616813],"Rose Bush":[2675410,2675411],"Rubidium":[2617070],"Ruthenium":[2617327],"Rutherfordium":[2617584],"Samarium":[2617841],"Sand":[2675667],"Sandstone":[2675924],"Sandstone Slab":[2676180,2676181,2676183],"Sandstone Stairs":[2676432,2676433,2676434,2676435,2676436,2676437,2676438,2676439],"Sandstone Wall":[2676487,2676496,2676497,2676498,2676499,2676500,2676501,2676502,2676503,2676504,2676505,2676506,2676507,2676508,2676509,2676510,2676511,2676544,2676545,2676546,2676547,2676548,2676549,2676550,2676551,2676552,2676553,2676554,2676555,2676556,2676557,2676558,2676559,2676560,2676561,2676562,2676563,2676564,2676565,2676566,2676567,2676568,2676569,2676570,2676571,2676572,2676573,2676574,2676575,2676576,2676577,2676578,2676579,2676580,2676581,2676582,2676583,2676584,2676585,2676586,2676587,2676588,2676589,2676590,2676591,2676592,2676593,2676594,2676595,2676596,2676597,2676598,2676599,2676600,2676601,2676602,2676603,2676604,2676605,2676606,2676607,2676615,2676624,2676625,2676626,2676627,2676628,2676629,2676630,2676631,2676632,2676633,2676634,2676635,2676636,2676637,2676638,2676639,2676672,2676673,2676674,2676675,2676676,2676677,2676678,2676679,2676680,2676681,2676682,2676683,2676684,2676685,2676686,2676687,2676688,2676689,2676690,2676691,2676692,2676693,2676694,2676695,2676696,2676697,2676698,2676699,2676700,2676701,2676702,2676703,2676704,2676705,2676706,2676707,2676708,2676709,2676710,2676711,2676712,2676713,2676714,2676715,2676716,2676717,2676718,2676719,2676720,2676721,2676722,2676723,2676724,2676725,2676726,2676727,2676728,2676729,2676730,2676731,2676732,2676733,2676734,2676735],"Scandium":[2618098],"Sculk":[2735035],"Sea Lantern":[2676952],"Sea Pickle":[2677208,2677209,2677210,2677211,2677212,2677213,2677214,2677215],"Seaborgium":[2618355],"Selenium":[2618612],"Shroomlight":[2712162],"Shulker Box":[2677466],"Silicon":[2618869],"Silver":[2619126],"Slime Block":[2677723],"Small Dripleaf":[2740944,2740945,2740946,2740947,2740948,2740949,2740950,2740951],"Smithing Table":[2730923],"Smoker":[2677976,2677977,2677978,2677979,2677980,2677981,2677982,2677983],"Smooth Basalt":[2699312],"Smooth Quartz Block":[2678237],"Smooth Quartz Slab":[2678492,2678494,2678495],"Smooth Quartz Stairs":[2678744,2678745,2678746,2678747,2678748,2678749,2678750,2678751],"Smooth Red Sandstone":[2679008],"Smooth Red Sandstone Slab":[2679264,2679265,2679267],"Smooth Red Sandstone Stairs":[2679520,2679521,2679522,2679523,2679524,2679525,2679526,2679527],"Smooth Sandstone":[2679779],"Smooth Sandstone Slab":[2680036,2680037,2680038],"Smooth Sandstone Stairs":[2680288,2680289,2680290,2680291,2680292,2680293,2680294,2680295],"Smooth Stone":[2680550],"Smooth Stone Slab":[2680805,2680806,2680807],"Snow Block":[2681064],"Snow Layer":[2681320,2681321,2681322,2681323,2681324,2681325,2681326,2681327],"Sodium":[2619383],"Soul Fire":[2711905],"Soul Lantern":[2711390,2711391],"Soul Sand":[2681578],"Soul Soil":[2711648],"Soul Torch":[2711130,2711131,2711132,2711133,2711135],"Sponge":[2681834,2681835],"Spore Blossom":[2731437],"Spruce Button":[2682080,2682081,2682084,2682085,2682086,2682087,2682088,2682089,2682092,2682093,2682094,2682095],"Spruce Door":[2682336,2682337,2682338,2682339,2682340,2682341,2682342,2682343,2682344,2682345,2682346,2682347,2682348,2682349,2682350,2682351,2682352,2682353,2682354,2682355,2682356,2682357,2682358,2682359,2682360,2682361,2682362,2682363,2682364,2682365,2682366,2682367],"Spruce Fence":[2682606],"Spruce Fence Gate":[2682848,2682849,2682850,2682851,2682852,2682853,2682854,2682855,2682856,2682857,2682858,2682859,2682860,2682861,2682862,2682863],"Spruce Leaves":[2683120,2683121,2683122,2683123],"Spruce Log":[2683376,2683377,2683378,2683379,2683380,2683381],"Spruce Planks":[2683634],"Spruce Pressure Plate":[2683890,2683891],"Spruce Sapling":[2684148,2684149],"Spruce Sign":[2684400,2684401,2684402,2684403,2684404,2684405,2684406,2684407,2684408,2684409,2684410,2684411,2684412,2684413,2684414,2684415],"Spruce Slab":[2684660,2684662,2684663],"Spruce Stairs":[2684912,2684913,2684914,2684915,2684916,2684917,2684918,2684919],"Spruce Trapdoor":[2685168,2685169,2685170,2685171,2685172,2685173,2685174,2685175,2685176,2685177,2685178,2685179,2685180,2685181,2685182,2685183],"Spruce Wall Sign":[2685432,2685433,2685434,2685435],"Spruce Wood":[2685688,2685689,2685690,2685691,2685694,2685695],"Stained Clay":[2685936,2685937,2685938,2685939,2685940,2685941,2685942,2685943,2685944,2685945,2685946,2685947,2685948,2685949,2685950,2685951],"Stained Glass":[2686192,2686193,2686194,2686195,2686196,2686197,2686198,2686199,2686200,2686201,2686202,2686203,2686204,2686205,2686206,2686207],"Stained Glass Pane":[2686448,2686449,2686450,2686451,2686452,2686453,2686454,2686455,2686456,2686457,2686458,2686459,2686460,2686461,2686462,2686463],"Stained Hardened Glass":[2686704,2686705,2686706,2686707,2686708,2686709,2686710,2686711,2686712,2686713,2686714,2686715,2686716,2686717,2686718,2686719],"Stained Hardened Glass Pane":[2686960,2686961,2686962,2686963,2686964,2686965,2686966,2686967,2686968,2686969,2686970,2686971,2686972,2686973,2686974,2686975],"Stone":[2686976],"Stone Brick Slab":[2687232,2687233,2687235],"Stone Brick Stairs":[2687488,2687489,2687490,2687491,2687492,2687493,2687494,2687495],"Stone Brick Wall":[2687744,2687745,2687746,2687747,2687748,2687749,2687750,2687751,2687752,2687753,2687754,2687755,2687756,2687757,2687758,2687759,2687760,2687761,2687762,2687763,2687764,2687765,2687766,2687767,2687768,2687769,2687770,2687771,2687772,2687773,2687774,2687775,2687776,2687777,2687778,2687779,2687780,2687781,2687782,2687783,2687784,2687785,2687786,2687787,2687788,2687789,2687790,2687791,2687792,2687793,2687794,2687795,2687796,2687797,2687798,2687799,2687800,2687801,2687802,2687803,2687804,2687805,2687806,2687807,2687808,2687809,2687810,2687811,2687812,2687813,2687814,2687815,2687816,2687817,2687818,2687819,2687820,2687821,2687822,2687823,2687827,2687872,2687873,2687874,2687875,2687876,2687877,2687878,2687879,2687880,2687881,2687882,2687883,2687884,2687885,2687886,2687887,2687888,2687889,2687890,2687891,2687892,2687893,2687894,2687895,2687896,2687897,2687898,2687899,2687900,2687901,2687902,2687903,2687904,2687905,2687906,2687907,2687908,2687909,2687910,2687911,2687912,2687913,2687914,2687915,2687916,2687917,2687918,2687919,2687920,2687921,2687922,2687923,2687924,2687925,2687926,2687927,2687928,2687929,2687930,2687931,2687932,2687933,2687934,2687935,2687936,2687937,2687938,2687939,2687940,2687941,2687942,2687943,2687944,2687945,2687946,2687947,2687948,2687949,2687950,2687951,2687955],"Stone Bricks":[2688004],"Stone Button":[2688256,2688257,2688260,2688261,2688262,2688263,2688264,2688265,2688268,2688269,2688270,2688271],"Stone Pressure Plate":[2688518,2688519],"Stone Slab":[2688773,2688774,2688775],"Stone Stairs":[2689032,2689033,2689034,2689035,2689036,2689037,2689038,2689039],"Stonecutter":[2689288,2689289,2689290,2689291],"Strontium":[2619640],"Sugarcane":[2692624,2692625,2692626,2692627,2692628,2692629,2692630,2692631,2692632,2692633,2692634,2692635,2692636,2692637,2692638,2692639],"Sulfur":[2619897],"Sunflower":[2692886,2692887],"Sweet Berry Bush":[2693144,2693145,2693146,2693147],"TNT":[2693656,2693657,2693658,2693659],"Tall Grass":[2693401],"Tantalum":[2620154],"Technetium":[2620411],"Tellurium":[2620668],"Tennessine":[2620925],"Terbium":[2621182],"Thallium":[2621439],"Thorium":[2621440],"Thulium":[2621697],"Tin":[2621954],"Tinted Glass":[2722442],"Titanium":[2622211],"Torch":[2693912,2693913,2693914,2693918,2693919],"Trapped Chest":[2694172,2694173,2694174,2694175],"Tripwire":[2694416,2694417,2694418,2694419,2694420,2694421,2694422,2694423,2694424,2694425,2694426,2694427,2694428,2694429,2694430,2694431],"Tripwire Hook":[2694672,2694673,2694674,2694675,2694676,2694677,2694678,2694679,2694680,2694681,2694682,2694683,2694684,2694685,2694686,2694687],"Tuff":[2710877],"Tungsten":[2622468],"Twisting Vines":[2734240,2734241,2734248,2734249,2734250,2734251,2734252,2734253,2734254,2734255,2734256,2734257,2734258,2734259,2734260,2734261,2734262,2734263,2734264,2734265,2734266,2734267,2734268,2734269,2734270,2734271],"Underwater Torch":[2694938,2694939,2694940,2694941,2694942],"Uranium":[2622725],"Vanadium":[2622982],"Vines":[2695200,2695201,2695202,2695203,2695204,2695205,2695206,2695207,2695208,2695209,2695210,2695211,2695212,2695213,2695214,2695215],"Wall Banner":[2695424,2695425,2695426,2695427,2695428,2695429,2695430,2695431,2695432,2695433,2695434,2695435,2695436,2695437,2695438,2695439,2695440,2695441,2695442,2695443,2695444,2695445,2695446,2695447,2695448,2695449,2695450,2695451,2695452,2695453,2695454,2695455,2695456,2695457,2695458,2695459,2695460,2695461,2695462,2695463,2695464,2695465,2695466,2695467,2695468,2695469,2695470,2695471,2695472,2695473,2695474,2695475,2695476,2695477,2695478,2695479,2695480,2695481,2695482,2695483,2695484,2695485,2695486,2695487],"Wall Coral Fan":[2695680,2695681,2695682,2695683,2695686,2695688,2695689,2695690,2695691,2695694,2695696,2695697,2695698,2695699,2695702,2695704,2695705,2695706,2695707,2695710,2695712,2695713,2695714,2695715,2695718,2695720,2695721,2695722,2695723,2695726,2695728,2695729,2695730,2695731,2695734,2695736,2695737,2695738,2695739,2695742],"Warped Button":[2717554,2717555,2717556,2717557,2717558,2717559,2717562,2717563,2717564,2717565,2717566,2717567],"Warped Door":[2719072,2719073,2719074,2719075,2719076,2719077,2719078,2719079,2719080,2719081,2719082,2719083,2719084,2719085,2719086,2719087,2719088,2719089,2719090,2719091,2719092,2719093,2719094,2719095,2719096,2719097,2719098,2719099,2719100,2719101,2719102,2719103],"Warped Fence":[2713704],"Warped Fence Gate":[2719872,2719873,2719874,2719875,2719876,2719877,2719878,2719879,2719880,2719881,2719882,2719883,2719884,2719885,2719886,2719887],"Warped Hyphae":[2716016,2716017,2716018,2716019,2716020,2716021],"Warped Planks":[2712933],"Warped Pressure Plate":[2718330,2718331],"Warped Sign":[2721408,2721409,2721410,2721411,2721412,2721413,2721414,2721415,2721416,2721417,2721418,2721419,2721420,2721421,2721422,2721423],"Warped Slab":[2714473,2714474,2714475],"Warped Stairs":[2720640,2720641,2720642,2720643,2720644,2720645,2720646,2720647],"Warped Stem":[2715242,2715243,2715244,2715245,2715246,2715247],"Warped Trapdoor":[2716784,2716785,2716786,2716787,2716788,2716789,2716790,2716791,2716792,2716793,2716794,2716795,2716796,2716797,2716798,2716799],"Warped Wall Sign":[2722184,2722185,2722186,2722187],"Warped Wart Block":[2727068],"Water":[2695968,2695969,2695970,2695971,2695972,2695973,2695974,2695975,2695976,2695977,2695978,2695979,2695980,2695981,2695982,2695983,2695984,2695985,2695986,2695987,2695988,2695989,2695990,2695991,2695992,2695993,2695994,2695995,2695996,2695997,2695998,2695999],"Water Cauldron":[2731946,2731947,2731948,2731949,2731950,2731951],"Weeping Vines":[2734496,2734497,2734504,2734505,2734506,2734507,2734508,2734509,2734510,2734511,2734512,2734513,2734514,2734515,2734516,2734517,2734518,2734519,2734520,2734521,2734522,2734523,2734524,2734525,2734526,2734527],"Weighted Pressure Plate Heavy":[2696224,2696225,2696226,2696227,2696228,2696229,2696230,2696231,2696232,2696233,2696234,2696235,2696236,2696237,2696238,2696239],"Weighted Pressure Plate Light":[2696480,2696481,2696482,2696483,2696484,2696485,2696486,2696487,2696488,2696489,2696490,2696491,2696492,2696493,2696494,2696495],"Wheat Block":[2696736,2696737,2696738,2696739,2696740,2696741,2696742,2696743],"White Tulip":[2697256],"Wither Rose":[2730152],"Wool":[2697504,2697505,2697506,2697507,2697508,2697509,2697510,2697511,2697512,2697513,2697514,2697515,2697516,2697517,2697518,2697519],"Xenon":[2623239],"Ytterbium":[2623496],"Yttrium":[2623753],"Zinc":[2624267],"Zirconium":[2624524],"ate!upd":[2637117],"reserved6":[2675153],"update!":[2636860]},"stateDataBits":8} \ No newline at end of file +{"knownStates":{"???":[2624010],"Acacia Button":[2560272,2560273,2560274,2560275,2560276,2560277,2560280,2560281,2560282,2560283,2560284,2560285],"Acacia Door":[2560512,2560513,2560514,2560515,2560516,2560517,2560518,2560519,2560520,2560521,2560522,2560523,2560524,2560525,2560526,2560527,2560528,2560529,2560530,2560531,2560532,2560533,2560534,2560535,2560536,2560537,2560538,2560539,2560540,2560541,2560542,2560543],"Acacia Fence":[2560787],"Acacia Fence Gate":[2561040,2561041,2561042,2561043,2561044,2561045,2561046,2561047,2561048,2561049,2561050,2561051,2561052,2561053,2561054,2561055],"Acacia Leaves":[2561300,2561301,2561302,2561303],"Acacia Log":[2561554,2561555,2561556,2561557,2561558,2561559],"Acacia Planks":[2561815],"Acacia Pressure Plate":[2562072,2562073],"Acacia Sapling":[2562328,2562329],"Acacia Sign":[2562576,2562577,2562578,2562579,2562580,2562581,2562582,2562583,2562584,2562585,2562586,2562587,2562588,2562589,2562590,2562591],"Acacia Slab":[2562841,2562842,2562843],"Acacia Stairs":[2563096,2563097,2563098,2563099,2563100,2563101,2563102,2563103],"Acacia Trapdoor":[2563344,2563345,2563346,2563347,2563348,2563349,2563350,2563351,2563352,2563353,2563354,2563355,2563356,2563357,2563358,2563359],"Acacia Wall Sign":[2563612,2563613,2563614,2563615],"Acacia Wood":[2563866,2563867,2563868,2563869,2563870,2563871],"Actinium":[2594197],"Activator Rail":[2564128,2564129,2564130,2564131,2564132,2564133,2564136,2564137,2564138,2564139,2564140,2564141],"Air":[2560016],"All Sided Mushroom Stem":[2564385],"Allium":[2564642],"Aluminum":[2594454],"Americium":[2594711],"Amethyst":[2698284],"Ancient Debris":[2698541],"Andesite":[2564899],"Andesite Slab":[2565156,2565157,2565158],"Andesite Stairs":[2565408,2565409,2565410,2565411,2565412,2565413,2565414,2565415],"Andesite Wall":[2565632,2565633,2565634,2565635,2565636,2565637,2565638,2565639,2565640,2565641,2565642,2565643,2565644,2565645,2565646,2565647,2565648,2565649,2565650,2565651,2565652,2565653,2565654,2565655,2565656,2565657,2565658,2565659,2565660,2565661,2565662,2565663,2565664,2565665,2565666,2565667,2565668,2565669,2565670,2565671,2565672,2565673,2565674,2565675,2565676,2565677,2565678,2565679,2565680,2565681,2565682,2565683,2565684,2565685,2565686,2565687,2565688,2565689,2565690,2565691,2565692,2565693,2565694,2565695,2565728,2565729,2565730,2565731,2565732,2565733,2565734,2565735,2565736,2565737,2565738,2565739,2565740,2565741,2565742,2565743,2565750,2565760,2565761,2565762,2565763,2565764,2565765,2565766,2565767,2565768,2565769,2565770,2565771,2565772,2565773,2565774,2565775,2565776,2565777,2565778,2565779,2565780,2565781,2565782,2565783,2565784,2565785,2565786,2565787,2565788,2565789,2565790,2565791,2565792,2565793,2565794,2565795,2565796,2565797,2565798,2565799,2565800,2565801,2565802,2565803,2565804,2565805,2565806,2565807,2565808,2565809,2565810,2565811,2565812,2565813,2565814,2565815,2565816,2565817,2565818,2565819,2565820,2565821,2565822,2565823,2565856,2565857,2565858,2565859,2565860,2565861,2565862,2565863,2565864,2565865,2565866,2565867,2565868,2565869,2565870,2565871,2565878],"Antimony":[2594968],"Anvil":[2565921,2565922,2565923,2565925,2565926,2565927,2565929,2565930,2565931,2565933,2565934,2565935],"Argon":[2595225],"Arsenic":[2595482],"Astatine":[2595739],"Azalea Leaves":[2735804,2735805,2735806,2735807],"Azure Bluet":[2566184],"Bamboo":[2566432,2566433,2566435,2566436,2566437,2566439,2566440,2566441,2566443,2566444,2566445,2566447],"Bamboo Sapling":[2566698,2566699],"Banner":[2566912,2566913,2566914,2566915,2566916,2566917,2566918,2566919,2566920,2566921,2566922,2566923,2566924,2566925,2566926,2566927,2566928,2566929,2566930,2566931,2566932,2566933,2566934,2566935,2566936,2566937,2566938,2566939,2566940,2566941,2566942,2566943,2566944,2566945,2566946,2566947,2566948,2566949,2566950,2566951,2566952,2566953,2566954,2566955,2566956,2566957,2566958,2566959,2566960,2566961,2566962,2566963,2566964,2566965,2566966,2566967,2566968,2566969,2566970,2566971,2566972,2566973,2566974,2566975,2566976,2566977,2566978,2566979,2566980,2566981,2566982,2566983,2566984,2566985,2566986,2566987,2566988,2566989,2566990,2566991,2566992,2566993,2566994,2566995,2566996,2566997,2566998,2566999,2567000,2567001,2567002,2567003,2567004,2567005,2567006,2567007,2567008,2567009,2567010,2567011,2567012,2567013,2567014,2567015,2567016,2567017,2567018,2567019,2567020,2567021,2567022,2567023,2567024,2567025,2567026,2567027,2567028,2567029,2567030,2567031,2567032,2567033,2567034,2567035,2567036,2567037,2567038,2567039,2567040,2567041,2567042,2567043,2567044,2567045,2567046,2567047,2567048,2567049,2567050,2567051,2567052,2567053,2567054,2567055,2567056,2567057,2567058,2567059,2567060,2567061,2567062,2567063,2567064,2567065,2567066,2567067,2567068,2567069,2567070,2567071,2567072,2567073,2567074,2567075,2567076,2567077,2567078,2567079,2567080,2567081,2567082,2567083,2567084,2567085,2567086,2567087,2567088,2567089,2567090,2567091,2567092,2567093,2567094,2567095,2567096,2567097,2567098,2567099,2567100,2567101,2567102,2567103,2567104,2567105,2567106,2567107,2567108,2567109,2567110,2567111,2567112,2567113,2567114,2567115,2567116,2567117,2567118,2567119,2567120,2567121,2567122,2567123,2567124,2567125,2567126,2567127,2567128,2567129,2567130,2567131,2567132,2567133,2567134,2567135,2567136,2567137,2567138,2567139,2567140,2567141,2567142,2567143,2567144,2567145,2567146,2567147,2567148,2567149,2567150,2567151,2567152,2567153,2567154,2567155,2567156,2567157,2567158,2567159,2567160,2567161,2567162,2567163,2567164,2567165,2567166,2567167],"Barium":[2595996],"Barrel":[2567200,2567201,2567204,2567205,2567206,2567207,2567208,2567209,2567212,2567213,2567214,2567215],"Barrier":[2567469],"Basalt":[2698796,2698798,2698799],"Beacon":[2567726],"Bed Block":[2567936,2567937,2567938,2567939,2567940,2567941,2567942,2567943,2567944,2567945,2567946,2567947,2567948,2567949,2567950,2567951,2567952,2567953,2567954,2567955,2567956,2567957,2567958,2567959,2567960,2567961,2567962,2567963,2567964,2567965,2567966,2567967,2567968,2567969,2567970,2567971,2567972,2567973,2567974,2567975,2567976,2567977,2567978,2567979,2567980,2567981,2567982,2567983,2567984,2567985,2567986,2567987,2567988,2567989,2567990,2567991,2567992,2567993,2567994,2567995,2567996,2567997,2567998,2567999,2568000,2568001,2568002,2568003,2568004,2568005,2568006,2568007,2568008,2568009,2568010,2568011,2568012,2568013,2568014,2568015,2568016,2568017,2568018,2568019,2568020,2568021,2568022,2568023,2568024,2568025,2568026,2568027,2568028,2568029,2568030,2568031,2568032,2568033,2568034,2568035,2568036,2568037,2568038,2568039,2568040,2568041,2568042,2568043,2568044,2568045,2568046,2568047,2568048,2568049,2568050,2568051,2568052,2568053,2568054,2568055,2568056,2568057,2568058,2568059,2568060,2568061,2568062,2568063,2568064,2568065,2568066,2568067,2568068,2568069,2568070,2568071,2568072,2568073,2568074,2568075,2568076,2568077,2568078,2568079,2568080,2568081,2568082,2568083,2568084,2568085,2568086,2568087,2568088,2568089,2568090,2568091,2568092,2568093,2568094,2568095,2568096,2568097,2568098,2568099,2568100,2568101,2568102,2568103,2568104,2568105,2568106,2568107,2568108,2568109,2568110,2568111,2568112,2568113,2568114,2568115,2568116,2568117,2568118,2568119,2568120,2568121,2568122,2568123,2568124,2568125,2568126,2568127,2568128,2568129,2568130,2568131,2568132,2568133,2568134,2568135,2568136,2568137,2568138,2568139,2568140,2568141,2568142,2568143,2568144,2568145,2568146,2568147,2568148,2568149,2568150,2568151,2568152,2568153,2568154,2568155,2568156,2568157,2568158,2568159,2568160,2568161,2568162,2568163,2568164,2568165,2568166,2568167,2568168,2568169,2568170,2568171,2568172,2568173,2568174,2568175,2568176,2568177,2568178,2568179,2568180,2568181,2568182,2568183,2568184,2568185,2568186,2568187,2568188,2568189,2568190,2568191],"Bedrock":[2568240,2568241],"Beetroot Block":[2568496,2568497,2568498,2568499,2568500,2568501,2568502,2568503],"Bell":[2568752,2568753,2568754,2568755,2568756,2568757,2568758,2568759,2568760,2568761,2568762,2568763,2568764,2568765,2568766,2568767],"Berkelium":[2596253],"Beryllium":[2596510],"Big Dripleaf":[2741200,2741201,2741202,2741203,2741204,2741205,2741206,2741207,2741208,2741209,2741210,2741211,2741212,2741213,2741214,2741215],"Big Dripleaf Stem":[2741460,2741461,2741462,2741463],"Birch Button":[2569008,2569009,2569010,2569011,2569014,2569015,2569016,2569017,2569018,2569019,2569022,2569023],"Birch Door":[2569248,2569249,2569250,2569251,2569252,2569253,2569254,2569255,2569256,2569257,2569258,2569259,2569260,2569261,2569262,2569263,2569264,2569265,2569266,2569267,2569268,2569269,2569270,2569271,2569272,2569273,2569274,2569275,2569276,2569277,2569278,2569279],"Birch Fence":[2569525],"Birch Fence Gate":[2569776,2569777,2569778,2569779,2569780,2569781,2569782,2569783,2569784,2569785,2569786,2569787,2569788,2569789,2569790,2569791],"Birch Leaves":[2570036,2570037,2570038,2570039],"Birch Log":[2570296,2570297,2570298,2570299,2570300,2570301],"Birch Planks":[2570553],"Birch Pressure Plate":[2570810,2570811],"Birch Sapling":[2571066,2571067],"Birch Sign":[2571312,2571313,2571314,2571315,2571316,2571317,2571318,2571319,2571320,2571321,2571322,2571323,2571324,2571325,2571326,2571327],"Birch Slab":[2571580,2571581,2571583],"Birch Stairs":[2571832,2571833,2571834,2571835,2571836,2571837,2571838,2571839],"Birch Trapdoor":[2572080,2572081,2572082,2572083,2572084,2572085,2572086,2572087,2572088,2572089,2572090,2572091,2572092,2572093,2572094,2572095],"Birch Wall Sign":[2572352,2572353,2572354,2572355],"Birch Wood":[2572608,2572609,2572610,2572611,2572612,2572613],"Bismuth":[2596767],"Blackstone":[2699569],"Blackstone Slab":[2699824,2699826,2699827],"Blackstone Stairs":[2700080,2700081,2700082,2700083,2700084,2700085,2700086,2700087],"Blackstone Wall":[2700288,2700289,2700290,2700291,2700292,2700293,2700294,2700295,2700296,2700297,2700298,2700299,2700300,2700301,2700302,2700303,2700304,2700305,2700306,2700307,2700308,2700309,2700310,2700311,2700312,2700313,2700314,2700315,2700316,2700317,2700318,2700319,2700320,2700321,2700322,2700323,2700324,2700325,2700326,2700327,2700328,2700329,2700330,2700331,2700332,2700333,2700334,2700335,2700336,2700337,2700338,2700339,2700340,2700341,2700342,2700343,2700344,2700345,2700346,2700347,2700348,2700349,2700350,2700351,2700388,2700400,2700401,2700402,2700403,2700404,2700405,2700406,2700407,2700408,2700409,2700410,2700411,2700412,2700413,2700414,2700415,2700416,2700417,2700418,2700419,2700420,2700421,2700422,2700423,2700424,2700425,2700426,2700427,2700428,2700429,2700430,2700431,2700432,2700433,2700434,2700435,2700436,2700437,2700438,2700439,2700440,2700441,2700442,2700443,2700444,2700445,2700446,2700447,2700448,2700449,2700450,2700451,2700452,2700453,2700454,2700455,2700456,2700457,2700458,2700459,2700460,2700461,2700462,2700463,2700464,2700465,2700466,2700467,2700468,2700469,2700470,2700471,2700472,2700473,2700474,2700475,2700476,2700477,2700478,2700479,2700516,2700528,2700529,2700530,2700531,2700532,2700533,2700534,2700535,2700536,2700537,2700538,2700539,2700540,2700541,2700542,2700543],"Blast Furnace":[2573120,2573121,2573122,2573123,2573124,2573125,2573126,2573127],"Blue Ice":[2573637],"Blue Orchid":[2573894],"Blue Torch":[2574146,2574147,2574148,2574149,2574150],"Bohrium":[2597024],"Bone Block":[2574408,2574409,2574410],"Bookshelf":[2574665],"Boron":[2597281],"Brewing Stand":[2574920,2574921,2574922,2574923,2574924,2574925,2574926,2574927],"Brick Slab":[2575177,2575178,2575179],"Brick Stairs":[2575432,2575433,2575434,2575435,2575436,2575437,2575438,2575439],"Brick Wall":[2575616,2575617,2575618,2575619,2575620,2575621,2575622,2575623,2575624,2575625,2575626,2575627,2575628,2575629,2575630,2575631,2575645,2575680,2575681,2575682,2575683,2575684,2575685,2575686,2575687,2575688,2575689,2575690,2575691,2575692,2575693,2575694,2575695,2575696,2575697,2575698,2575699,2575700,2575701,2575702,2575703,2575704,2575705,2575706,2575707,2575708,2575709,2575710,2575711,2575712,2575713,2575714,2575715,2575716,2575717,2575718,2575719,2575720,2575721,2575722,2575723,2575724,2575725,2575726,2575727,2575728,2575729,2575730,2575731,2575732,2575733,2575734,2575735,2575736,2575737,2575738,2575739,2575740,2575741,2575742,2575743,2575744,2575745,2575746,2575747,2575748,2575749,2575750,2575751,2575752,2575753,2575754,2575755,2575756,2575757,2575758,2575759,2575773,2575808,2575809,2575810,2575811,2575812,2575813,2575814,2575815,2575816,2575817,2575818,2575819,2575820,2575821,2575822,2575823,2575824,2575825,2575826,2575827,2575828,2575829,2575830,2575831,2575832,2575833,2575834,2575835,2575836,2575837,2575838,2575839,2575840,2575841,2575842,2575843,2575844,2575845,2575846,2575847,2575848,2575849,2575850,2575851,2575852,2575853,2575854,2575855,2575856,2575857,2575858,2575859,2575860,2575861,2575862,2575863,2575864,2575865,2575866,2575867,2575868,2575869,2575870,2575871],"Bricks":[2575950],"Bromine":[2597538],"Brown Mushroom":[2576464],"Brown Mushroom Block":[2576720,2576721,2576722,2576723,2576724,2576725,2576726,2576727,2576728,2576729,2576731],"Cactus":[2576976,2576977,2576978,2576979,2576980,2576981,2576982,2576983,2576984,2576985,2576986,2576987,2576988,2576989,2576990,2576991],"Cadmium":[2597795],"Cake":[2577232,2577233,2577234,2577235,2577237,2577238,2577239],"Cake With Candle":[2729638,2729639],"Cake With Dyed Candle":[2729888,2729889,2729890,2729891,2729892,2729893,2729894,2729895,2729896,2729897,2729898,2729899,2729900,2729901,2729902,2729903,2729904,2729905,2729906,2729907,2729908,2729909,2729910,2729911,2729912,2729913,2729914,2729915,2729916,2729917,2729918,2729919],"Calcite":[2704709],"Calcium":[2598052],"Californium":[2598309],"Candle":[2729120,2729121,2729122,2729123,2729124,2729125,2729126,2729127],"Carbon":[2598566],"Carpet":[2577488,2577489,2577490,2577491,2577492,2577493,2577494,2577495,2577496,2577497,2577498,2577499,2577500,2577501,2577502,2577503],"Carrot Block":[2577744,2577745,2577746,2577747,2577748,2577749,2577750,2577751],"Cartography Table":[2730666],"Carved Pumpkin":[2578004,2578005,2578006,2578007],"Cauldron":[2731694],"Cave Vines":[2736512,2736513,2736514,2736515,2736516,2736517,2736518,2736519,2736520,2736521,2736522,2736523,2736524,2736525,2736526,2736527,2736528,2736529,2736530,2736531,2736532,2736533,2736534,2736535,2736536,2736537,2736544,2736545,2736546,2736547,2736548,2736549,2736550,2736551,2736552,2736553,2736554,2736555,2736556,2736557,2736558,2736559,2736560,2736561,2736562,2736563,2736564,2736565,2736566,2736567,2736568,2736569,2736576,2736577,2736578,2736579,2736580,2736581,2736582,2736583,2736584,2736585,2736586,2736587,2736588,2736589,2736590,2736591,2736592,2736593,2736594,2736595,2736596,2736597,2736598,2736599,2736600,2736601,2736608,2736609,2736610,2736611,2736612,2736613,2736614,2736615,2736616,2736617,2736618,2736619,2736620,2736621,2736622,2736623,2736624,2736625,2736626,2736627,2736628,2736629,2736630,2736631,2736632,2736633],"Cerium":[2598823],"Cesium":[2599080],"Chain":[2734776,2734778,2734779],"Cherry Button":[2737088,2737089,2737090,2737091,2737094,2737095,2737096,2737097,2737098,2737099,2737102,2737103],"Cherry Door":[2737344,2737345,2737346,2737347,2737348,2737349,2737350,2737351,2737352,2737353,2737354,2737355,2737356,2737357,2737358,2737359,2737360,2737361,2737362,2737363,2737364,2737365,2737366,2737367,2737368,2737369,2737370,2737371,2737372,2737373,2737374,2737375],"Cherry Fence":[2737605],"Cherry Fence Gate":[2737856,2737857,2737858,2737859,2737860,2737861,2737862,2737863,2737864,2737865,2737866,2737867,2737868,2737869,2737870,2737871],"Cherry Leaves":[2738116,2738117,2738118,2738119],"Cherry Log":[2738376,2738377,2738378,2738379,2738380,2738381],"Cherry Planks":[2738633],"Cherry Pressure Plate":[2738890,2738891],"Cherry Sign":[2739392,2739393,2739394,2739395,2739396,2739397,2739398,2739399,2739400,2739401,2739402,2739403,2739404,2739405,2739406,2739407],"Cherry Slab":[2739660,2739661,2739663],"Cherry Stairs":[2739912,2739913,2739914,2739915,2739916,2739917,2739918,2739919],"Cherry Trapdoor":[2740160,2740161,2740162,2740163,2740164,2740165,2740166,2740167,2740168,2740169,2740170,2740171,2740172,2740173,2740174,2740175],"Cherry Wall Sign":[2740432,2740433,2740434,2740435],"Cherry Wood":[2740688,2740689,2740690,2740691,2740692,2740693],"Chest":[2578520,2578521,2578522,2578523],"Chiseled Deepslate":[2710106],"Chiseled Nether Bricks":[2710363],"Chiseled Polished Blackstone":[2702139],"Chiseled Quartz Block":[2578776,2578777,2578779],"Chiseled Red Sandstone":[2579034],"Chiseled Sandstone":[2579291],"Chiseled Stone Bricks":[2579548],"Chlorine":[2599337],"Chorus Flower":[2732976,2732977,2732978,2732979,2732982,2732983],"Chorus Plant":[2733236],"Chromium":[2599594],"Clay Block":[2579805],"Coal Block":[2580062],"Coal Ore":[2580319],"Cobalt":[2599851],"Cobbled Deepslate":[2707793],"Cobbled Deepslate Slab":[2708048,2708050,2708051],"Cobbled Deepslate Stairs":[2708304,2708305,2708306,2708307,2708308,2708309,2708310,2708311],"Cobbled Deepslate Wall":[2708484,2708496,2708497,2708498,2708499,2708500,2708501,2708502,2708503,2708504,2708505,2708506,2708507,2708508,2708509,2708510,2708511,2708544,2708545,2708546,2708547,2708548,2708549,2708550,2708551,2708552,2708553,2708554,2708555,2708556,2708557,2708558,2708559,2708560,2708561,2708562,2708563,2708564,2708565,2708566,2708567,2708568,2708569,2708570,2708571,2708572,2708573,2708574,2708575,2708576,2708577,2708578,2708579,2708580,2708581,2708582,2708583,2708584,2708585,2708586,2708587,2708588,2708589,2708590,2708591,2708592,2708593,2708594,2708595,2708596,2708597,2708598,2708599,2708600,2708601,2708602,2708603,2708604,2708605,2708606,2708607,2708612,2708624,2708625,2708626,2708627,2708628,2708629,2708630,2708631,2708632,2708633,2708634,2708635,2708636,2708637,2708638,2708639,2708672,2708673,2708674,2708675,2708676,2708677,2708678,2708679,2708680,2708681,2708682,2708683,2708684,2708685,2708686,2708687,2708688,2708689,2708690,2708691,2708692,2708693,2708694,2708695,2708696,2708697,2708698,2708699,2708700,2708701,2708702,2708703,2708704,2708705,2708706,2708707,2708708,2708709,2708710,2708711,2708712,2708713,2708714,2708715,2708716,2708717,2708718,2708719,2708720,2708721,2708722,2708723,2708724,2708725,2708726,2708727,2708728,2708729,2708730,2708731,2708732,2708733,2708734,2708735],"Cobblestone":[2580576],"Cobblestone Slab":[2580832,2580833,2580835],"Cobblestone Stairs":[2581088,2581089,2581090,2581091,2581092,2581093,2581094,2581095],"Cobblestone Wall":[2581280,2581281,2581282,2581283,2581284,2581285,2581286,2581287,2581288,2581289,2581290,2581291,2581292,2581293,2581294,2581295,2581299,2581312,2581313,2581314,2581315,2581316,2581317,2581318,2581319,2581320,2581321,2581322,2581323,2581324,2581325,2581326,2581327,2581328,2581329,2581330,2581331,2581332,2581333,2581334,2581335,2581336,2581337,2581338,2581339,2581340,2581341,2581342,2581343,2581344,2581345,2581346,2581347,2581348,2581349,2581350,2581351,2581352,2581353,2581354,2581355,2581356,2581357,2581358,2581359,2581360,2581361,2581362,2581363,2581364,2581365,2581366,2581367,2581368,2581369,2581370,2581371,2581372,2581373,2581374,2581375,2581408,2581409,2581410,2581411,2581412,2581413,2581414,2581415,2581416,2581417,2581418,2581419,2581420,2581421,2581422,2581423,2581427,2581440,2581441,2581442,2581443,2581444,2581445,2581446,2581447,2581448,2581449,2581450,2581451,2581452,2581453,2581454,2581455,2581456,2581457,2581458,2581459,2581460,2581461,2581462,2581463,2581464,2581465,2581466,2581467,2581468,2581469,2581470,2581471,2581472,2581473,2581474,2581475,2581476,2581477,2581478,2581479,2581480,2581481,2581482,2581483,2581484,2581485,2581486,2581487,2581488,2581489,2581490,2581491,2581492,2581493,2581494,2581495,2581496,2581497,2581498,2581499,2581500,2581501,2581502,2581503],"Cobweb":[2581604],"Cocoa Block":[2581856,2581857,2581858,2581859,2581860,2581861,2581862,2581863,2581868,2581869,2581870,2581871],"Compound Creator":[2582116,2582117,2582118,2582119],"Concrete":[2582368,2582369,2582370,2582371,2582372,2582373,2582374,2582375,2582376,2582377,2582378,2582379,2582380,2582381,2582382,2582383],"Concrete Powder":[2582624,2582625,2582626,2582627,2582628,2582629,2582630,2582631,2582632,2582633,2582634,2582635,2582636,2582637,2582638,2582639],"Copernicium":[2600365],"Copper":[2600622],"Copper Block":[2728096,2728097,2728098,2728099,2728100,2728101,2728102,2728103],"Copper Ore":[2725012],"Coral":[2582880,2582881,2582882,2582883,2582885,2582888,2582889,2582890,2582891,2582893],"Coral Block":[2583136,2583137,2583138,2583139,2583142,2583144,2583145,2583146,2583147,2583150],"Coral Fan":[2583392,2583393,2583394,2583395,2583399,2583400,2583401,2583402,2583403,2583407,2583408,2583409,2583410,2583411,2583415,2583416,2583417,2583418,2583419,2583423],"Cornflower":[2583660],"Cracked Deepslate Bricks":[2706251],"Cracked Deepslate Tiles":[2707536],"Cracked Nether Bricks":[2710620],"Cracked Polished Blackstone Bricks":[2703424],"Cracked Stone Bricks":[2583917],"Crafting Table":[2584174],"Crimson Button":[2717298,2717299,2717300,2717301,2717302,2717303,2717306,2717307,2717308,2717309,2717310,2717311],"Crimson Door":[2718816,2718817,2718818,2718819,2718820,2718821,2718822,2718823,2718824,2718825,2718826,2718827,2718828,2718829,2718830,2718831,2718832,2718833,2718834,2718835,2718836,2718837,2718838,2718839,2718840,2718841,2718842,2718843,2718844,2718845,2718846,2718847],"Crimson Fence":[2713447],"Crimson Fence Gate":[2719600,2719601,2719602,2719603,2719604,2719605,2719606,2719607,2719608,2719609,2719610,2719611,2719612,2719613,2719614,2719615],"Crimson Hyphae":[2715760,2715761,2715762,2715763,2715764,2715765],"Crimson Planks":[2712676],"Crimson Pressure Plate":[2718072,2718073],"Crimson Sign":[2721152,2721153,2721154,2721155,2721156,2721157,2721158,2721159,2721160,2721161,2721162,2721163,2721164,2721165,2721166,2721167],"Crimson Slab":[2714216,2714218,2714219],"Crimson Stairs":[2720384,2720385,2720386,2720387,2720388,2720389,2720390,2720391],"Crimson Stem":[2714984,2714985,2714988,2714989,2714990,2714991],"Crimson Trapdoor":[2716528,2716529,2716530,2716531,2716532,2716533,2716534,2716535,2716536,2716537,2716538,2716539,2716540,2716541,2716542,2716543],"Crimson Wall Sign":[2721928,2721929,2721930,2721931],"Crying Obsidian":[2727325],"Curium":[2600879],"Cut Copper Block":[2728352,2728353,2728354,2728355,2728356,2728357,2728358,2728359],"Cut Copper Slab Slab":[2728608,2728609,2728610,2728611,2728612,2728613,2728614,2728615,2728616,2728617,2728618,2728619,2728620,2728621,2728622,2728623,2728624,2728625,2728626,2728627,2728628,2728629,2728630,2728631],"Cut Copper Stairs":[2728832,2728833,2728834,2728835,2728836,2728837,2728838,2728839,2728840,2728841,2728842,2728843,2728844,2728845,2728846,2728847,2728848,2728849,2728850,2728851,2728852,2728853,2728854,2728855,2728856,2728857,2728858,2728859,2728860,2728861,2728862,2728863,2728864,2728865,2728866,2728867,2728868,2728869,2728870,2728871,2728872,2728873,2728874,2728875,2728876,2728877,2728878,2728879,2728880,2728881,2728882,2728883,2728884,2728885,2728886,2728887,2728888,2728889,2728890,2728891,2728892,2728893,2728894,2728895],"Cut Red Sandstone":[2584431],"Cut Red Sandstone Slab":[2584688,2584689,2584690],"Cut Sandstone":[2584945],"Cut Sandstone Slab":[2585200,2585202,2585203],"Dandelion":[2585716],"Dark Oak Button":[2585968,2585969,2585972,2585973,2585974,2585975,2585976,2585977,2585980,2585981,2585982,2585983],"Dark Oak Door":[2586208,2586209,2586210,2586211,2586212,2586213,2586214,2586215,2586216,2586217,2586218,2586219,2586220,2586221,2586222,2586223,2586224,2586225,2586226,2586227,2586228,2586229,2586230,2586231,2586232,2586233,2586234,2586235,2586236,2586237,2586238,2586239],"Dark Oak Fence":[2586487],"Dark Oak Fence Gate":[2586736,2586737,2586738,2586739,2586740,2586741,2586742,2586743,2586744,2586745,2586746,2586747,2586748,2586749,2586750,2586751],"Dark Oak Leaves":[2587000,2587001,2587002,2587003],"Dark Oak Log":[2587256,2587257,2587258,2587259,2587262,2587263],"Dark Oak Planks":[2587515],"Dark Oak Pressure Plate":[2587772,2587773],"Dark Oak Sapling":[2588028,2588029],"Dark Oak Sign":[2588272,2588273,2588274,2588275,2588276,2588277,2588278,2588279,2588280,2588281,2588282,2588283,2588284,2588285,2588286,2588287],"Dark Oak Slab":[2588541,2588542,2588543],"Dark Oak Stairs":[2588800,2588801,2588802,2588803,2588804,2588805,2588806,2588807],"Dark Oak Trapdoor":[2589056,2589057,2589058,2589059,2589060,2589061,2589062,2589063,2589064,2589065,2589066,2589067,2589068,2589069,2589070,2589071],"Dark Oak Wall Sign":[2589312,2589313,2589314,2589315],"Dark Oak Wood":[2589568,2589569,2589570,2589571,2589574,2589575],"Dark Prismarine":[2589828],"Dark Prismarine Slab":[2590084,2590085,2590087],"Dark Prismarine Stairs":[2590336,2590337,2590338,2590339,2590340,2590341,2590342,2590343],"Darmstadtium":[2601136],"Daylight Sensor":[2590592,2590593,2590594,2590595,2590596,2590597,2590598,2590599,2590600,2590601,2590602,2590603,2590604,2590605,2590606,2590607,2590608,2590609,2590610,2590611,2590612,2590613,2590614,2590615,2590616,2590617,2590618,2590619,2590620,2590621,2590622,2590623],"Dead Bush":[2590856],"Deepslate":[2704964,2704966,2704967],"Deepslate Brick Slab":[2705480,2705481,2705482],"Deepslate Brick Stairs":[2705736,2705737,2705738,2705739,2705740,2705741,2705742,2705743],"Deepslate Brick Wall":[2705920,2705921,2705922,2705923,2705924,2705925,2705926,2705927,2705928,2705929,2705930,2705931,2705932,2705933,2705934,2705935,2705946,2705984,2705985,2705986,2705987,2705988,2705989,2705990,2705991,2705992,2705993,2705994,2705995,2705996,2705997,2705998,2705999,2706000,2706001,2706002,2706003,2706004,2706005,2706006,2706007,2706008,2706009,2706010,2706011,2706012,2706013,2706014,2706015,2706016,2706017,2706018,2706019,2706020,2706021,2706022,2706023,2706024,2706025,2706026,2706027,2706028,2706029,2706030,2706031,2706032,2706033,2706034,2706035,2706036,2706037,2706038,2706039,2706040,2706041,2706042,2706043,2706044,2706045,2706046,2706047,2706048,2706049,2706050,2706051,2706052,2706053,2706054,2706055,2706056,2706057,2706058,2706059,2706060,2706061,2706062,2706063,2706074,2706112,2706113,2706114,2706115,2706116,2706117,2706118,2706119,2706120,2706121,2706122,2706123,2706124,2706125,2706126,2706127,2706128,2706129,2706130,2706131,2706132,2706133,2706134,2706135,2706136,2706137,2706138,2706139,2706140,2706141,2706142,2706143,2706144,2706145,2706146,2706147,2706148,2706149,2706150,2706151,2706152,2706153,2706154,2706155,2706156,2706157,2706158,2706159,2706160,2706161,2706162,2706163,2706164,2706165,2706166,2706167,2706168,2706169,2706170,2706171,2706172,2706173,2706174,2706175],"Deepslate Bricks":[2705223],"Deepslate Coal Ore":[2722956],"Deepslate Copper Ore":[2724755],"Deepslate Diamond Ore":[2723213],"Deepslate Emerald Ore":[2723470],"Deepslate Gold Ore":[2724498],"Deepslate Iron Ore":[2724241],"Deepslate Lapis Lazuli Ore":[2723727],"Deepslate Redstone Ore":[2723984,2723985],"Deepslate Tile Slab":[2706764,2706765,2706767],"Deepslate Tile Stairs":[2707016,2707017,2707018,2707019,2707020,2707021,2707022,2707023],"Deepslate Tile Wall":[2707200,2707201,2707202,2707203,2707204,2707205,2707206,2707207,2707208,2707209,2707210,2707211,2707212,2707213,2707214,2707215,2707231,2707264,2707265,2707266,2707267,2707268,2707269,2707270,2707271,2707272,2707273,2707274,2707275,2707276,2707277,2707278,2707279,2707280,2707281,2707282,2707283,2707284,2707285,2707286,2707287,2707288,2707289,2707290,2707291,2707292,2707293,2707294,2707295,2707296,2707297,2707298,2707299,2707300,2707301,2707302,2707303,2707304,2707305,2707306,2707307,2707308,2707309,2707310,2707311,2707312,2707313,2707314,2707315,2707316,2707317,2707318,2707319,2707320,2707321,2707322,2707323,2707324,2707325,2707326,2707327,2707328,2707329,2707330,2707331,2707332,2707333,2707334,2707335,2707336,2707337,2707338,2707339,2707340,2707341,2707342,2707343,2707359,2707392,2707393,2707394,2707395,2707396,2707397,2707398,2707399,2707400,2707401,2707402,2707403,2707404,2707405,2707406,2707407,2707408,2707409,2707410,2707411,2707412,2707413,2707414,2707415,2707416,2707417,2707418,2707419,2707420,2707421,2707422,2707423,2707424,2707425,2707426,2707427,2707428,2707429,2707430,2707431,2707432,2707433,2707434,2707435,2707436,2707437,2707438,2707439,2707440,2707441,2707442,2707443,2707444,2707445,2707446,2707447,2707448,2707449,2707450,2707451,2707452,2707453,2707454,2707455],"Deepslate Tiles":[2706508],"Detector Rail":[2591104,2591105,2591106,2591107,2591108,2591109,2591112,2591113,2591114,2591115,2591116,2591117],"Diamond Block":[2591370],"Diamond Ore":[2591627],"Diorite":[2591884],"Diorite Slab":[2592140,2592141,2592143],"Diorite Stairs":[2592392,2592393,2592394,2592395,2592396,2592397,2592398,2592399],"Diorite Wall":[2592512,2592513,2592514,2592515,2592516,2592517,2592518,2592519,2592520,2592521,2592522,2592523,2592524,2592525,2592526,2592527,2592528,2592529,2592530,2592531,2592532,2592533,2592534,2592535,2592536,2592537,2592538,2592539,2592540,2592541,2592542,2592543,2592544,2592545,2592546,2592547,2592548,2592549,2592550,2592551,2592552,2592553,2592554,2592555,2592556,2592557,2592558,2592559,2592560,2592561,2592562,2592563,2592564,2592565,2592566,2592567,2592568,2592569,2592570,2592571,2592572,2592573,2592574,2592575,2592576,2592577,2592578,2592579,2592580,2592581,2592582,2592583,2592584,2592585,2592586,2592587,2592588,2592589,2592590,2592591,2592607,2592640,2592641,2592642,2592643,2592644,2592645,2592646,2592647,2592648,2592649,2592650,2592651,2592652,2592653,2592654,2592655,2592656,2592657,2592658,2592659,2592660,2592661,2592662,2592663,2592664,2592665,2592666,2592667,2592668,2592669,2592670,2592671,2592672,2592673,2592674,2592675,2592676,2592677,2592678,2592679,2592680,2592681,2592682,2592683,2592684,2592685,2592686,2592687,2592688,2592689,2592690,2592691,2592692,2592693,2592694,2592695,2592696,2592697,2592698,2592699,2592700,2592701,2592702,2592703,2592704,2592705,2592706,2592707,2592708,2592709,2592710,2592711,2592712,2592713,2592714,2592715,2592716,2592717,2592718,2592719,2592735],"Dirt":[2592912,2592913,2592914],"Double Tallgrass":[2593168,2593169],"Dragon Egg":[2593426],"Dried Kelp Block":[2593683],"Dubnium":[2601393],"Dyed Candle":[2729344,2729345,2729346,2729347,2729348,2729349,2729350,2729351,2729352,2729353,2729354,2729355,2729356,2729357,2729358,2729359,2729360,2729361,2729362,2729363,2729364,2729365,2729366,2729367,2729368,2729369,2729370,2729371,2729372,2729373,2729374,2729375,2729376,2729377,2729378,2729379,2729380,2729381,2729382,2729383,2729384,2729385,2729386,2729387,2729388,2729389,2729390,2729391,2729392,2729393,2729394,2729395,2729396,2729397,2729398,2729399,2729400,2729401,2729402,2729403,2729404,2729405,2729406,2729407,2729408,2729409,2729410,2729411,2729412,2729413,2729414,2729415,2729416,2729417,2729418,2729419,2729420,2729421,2729422,2729423,2729424,2729425,2729426,2729427,2729428,2729429,2729430,2729431,2729432,2729433,2729434,2729435,2729436,2729437,2729438,2729439,2729440,2729441,2729442,2729443,2729444,2729445,2729446,2729447,2729448,2729449,2729450,2729451,2729452,2729453,2729454,2729455,2729456,2729457,2729458,2729459,2729460,2729461,2729462,2729463,2729464,2729465,2729466,2729467,2729468,2729469,2729470,2729471],"Dyed Shulker Box":[2593936,2593937,2593938,2593939,2593940,2593941,2593942,2593943,2593944,2593945,2593946,2593947,2593948,2593949,2593950,2593951],"Dysprosium":[2601650],"Einsteinium":[2601907],"Element Constructor":[2600108,2600109,2600110,2600111],"Emerald Block":[2624781],"Emerald Ore":[2625038],"Enchanting Table":[2625295],"End Portal Frame":[2625552,2625553,2625554,2625555,2625556,2625557,2625558,2625559],"End Rod":[2625808,2625809,2625810,2625811,2625812,2625813],"End Stone":[2626066],"End Stone Brick Slab":[2626321,2626322,2626323],"End Stone Brick Stairs":[2626576,2626577,2626578,2626579,2626580,2626581,2626582,2626583],"End Stone Brick Wall":[2626816,2626817,2626818,2626819,2626820,2626821,2626822,2626823,2626824,2626825,2626826,2626827,2626828,2626829,2626830,2626831,2626832,2626833,2626834,2626835,2626836,2626837,2626838,2626839,2626840,2626841,2626842,2626843,2626844,2626845,2626846,2626847,2626848,2626849,2626850,2626851,2626852,2626853,2626854,2626855,2626856,2626857,2626858,2626859,2626860,2626861,2626862,2626863,2626864,2626865,2626866,2626867,2626868,2626869,2626870,2626871,2626872,2626873,2626874,2626875,2626876,2626877,2626878,2626879,2626885,2626896,2626897,2626898,2626899,2626900,2626901,2626902,2626903,2626904,2626905,2626906,2626907,2626908,2626909,2626910,2626911,2626944,2626945,2626946,2626947,2626948,2626949,2626950,2626951,2626952,2626953,2626954,2626955,2626956,2626957,2626958,2626959,2626960,2626961,2626962,2626963,2626964,2626965,2626966,2626967,2626968,2626969,2626970,2626971,2626972,2626973,2626974,2626975,2626976,2626977,2626978,2626979,2626980,2626981,2626982,2626983,2626984,2626985,2626986,2626987,2626988,2626989,2626990,2626991,2626992,2626993,2626994,2626995,2626996,2626997,2626998,2626999,2627000,2627001,2627002,2627003,2627004,2627005,2627006,2627007,2627013,2627024,2627025,2627026,2627027,2627028,2627029,2627030,2627031,2627032,2627033,2627034,2627035,2627036,2627037,2627038,2627039],"End Stone Bricks":[2627094],"Ender Chest":[2627348,2627349,2627350,2627351],"Erbium":[2602164],"Europium":[2602421],"Fake Wooden Slab":[2627608,2627609,2627610],"Farmland":[2627864,2627865,2627866,2627867,2627868,2627869,2627870,2627871],"Fermium":[2602678],"Fern":[2628122],"Fire Block":[2628368,2628369,2628370,2628371,2628372,2628373,2628374,2628375,2628376,2628377,2628378,2628379,2628380,2628381,2628382,2628383],"Flerovium":[2602935],"Fletching Table":[2628636],"Flower Pot":[2628893],"Flowering Azalea Leaves":[2736060,2736061,2736062,2736063],"Fluorine":[2603192],"Francium":[2603449],"Froglight":[2734001,2734002,2734003,2734005,2734006,2734007,2734013,2734014,2734015],"Frosted Ice":[2629148,2629149,2629150,2629151],"Furnace":[2629400,2629401,2629402,2629403,2629404,2629405,2629406,2629407],"Gadolinium":[2603706],"Gallium":[2603963],"Germanium":[2604220],"Gilded Blackstone":[2727582],"Glass":[2629664],"Glass Pane":[2629921],"Glazed Terracotta":[2697984,2697985,2697986,2697987,2697988,2697989,2697990,2697991,2697992,2697993,2697994,2697995,2697996,2697997,2697998,2697999,2698000,2698001,2698002,2698003,2698004,2698005,2698006,2698007,2698008,2698009,2698010,2698011,2698012,2698013,2698014,2698015,2698016,2698017,2698018,2698019,2698020,2698021,2698022,2698023,2698024,2698025,2698026,2698027,2698028,2698029,2698030,2698031,2698032,2698033,2698034,2698035,2698036,2698037,2698038,2698039,2698040,2698041,2698042,2698043,2698044,2698045,2698046,2698047],"Glow Item Frame":[2735280,2735281,2735284,2735285,2735286,2735287,2735288,2735289,2735292,2735293,2735294,2735295],"Glow Lichen":[2736832,2736833,2736834,2736835,2736836,2736837,2736838,2736839,2736840,2736841,2736842,2736843,2736844,2736845,2736846,2736847,2736848,2736849,2736850,2736851,2736852,2736853,2736854,2736855,2736856,2736857,2736858,2736859,2736860,2736861,2736862,2736863,2736864,2736865,2736866,2736867,2736868,2736869,2736870,2736871,2736872,2736873,2736874,2736875,2736876,2736877,2736878,2736879,2736880,2736881,2736882,2736883,2736884,2736885,2736886,2736887,2736888,2736889,2736890,2736891,2736892,2736893,2736894,2736895],"Glowing Obsidian":[2630178],"Glowstone":[2630435],"Gold":[2604477],"Gold Block":[2630692],"Gold Ore":[2630949],"Granite":[2631206],"Granite Slab":[2631461,2631462,2631463],"Granite Stairs":[2631720,2631721,2631722,2631723,2631724,2631725,2631726,2631727],"Granite Wall":[2631936,2631937,2631938,2631939,2631940,2631941,2631942,2631943,2631944,2631945,2631946,2631947,2631948,2631949,2631950,2631951,2631952,2631953,2631954,2631955,2631956,2631957,2631958,2631959,2631960,2631961,2631962,2631963,2631964,2631965,2631966,2631967,2631968,2631969,2631970,2631971,2631972,2631973,2631974,2631975,2631976,2631977,2631978,2631979,2631980,2631981,2631982,2631983,2631984,2631985,2631986,2631987,2631988,2631989,2631990,2631991,2631992,2631993,2631994,2631995,2631996,2631997,2631998,2631999,2632032,2632033,2632034,2632035,2632036,2632037,2632038,2632039,2632040,2632041,2632042,2632043,2632044,2632045,2632046,2632047,2632057,2632064,2632065,2632066,2632067,2632068,2632069,2632070,2632071,2632072,2632073,2632074,2632075,2632076,2632077,2632078,2632079,2632080,2632081,2632082,2632083,2632084,2632085,2632086,2632087,2632088,2632089,2632090,2632091,2632092,2632093,2632094,2632095,2632096,2632097,2632098,2632099,2632100,2632101,2632102,2632103,2632104,2632105,2632106,2632107,2632108,2632109,2632110,2632111,2632112,2632113,2632114,2632115,2632116,2632117,2632118,2632119,2632120,2632121,2632122,2632123,2632124,2632125,2632126,2632127,2632160,2632161,2632162,2632163,2632164,2632165,2632166,2632167,2632168,2632169,2632170,2632171,2632172,2632173,2632174,2632175,2632185],"Grass":[2632234],"Grass Path":[2632491],"Gravel":[2632748],"Green Torch":[2633514,2633515,2633516,2633517,2633518],"Hafnium":[2604734],"Hanging Roots":[2730409],"Hardened Clay":[2633776],"Hardened Glass":[2634033],"Hardened Glass Pane":[2634290],"Hassium":[2604991],"Hay Bale":[2634545,2634546,2634547],"Heat Block":[2578263],"Helium":[2605248],"Holmium":[2605505],"Honeycomb Block":[2722699],"Hopper":[2634800,2634801,2634804,2634806,2634807,2634808,2634809,2634812,2634814,2634815],"Hydrogen":[2605762],"Ice":[2635061],"Indium":[2606019],"Infested Chiseled Stone Brick":[2635318],"Infested Cobblestone":[2635575],"Infested Cracked Stone Brick":[2635832],"Infested Mossy Stone Brick":[2636089],"Infested Stone":[2636346],"Infested Stone Brick":[2636603],"Invisible Bedrock":[2637374],"Iodine":[2606276],"Iridium":[2606533],"Iron":[2606790],"Iron Bars":[2637888],"Iron Block":[2637631],"Iron Door":[2638144,2638145,2638146,2638147,2638148,2638149,2638150,2638151,2638152,2638153,2638154,2638155,2638156,2638157,2638158,2638159,2638160,2638161,2638162,2638163,2638164,2638165,2638166,2638167,2638168,2638169,2638170,2638171,2638172,2638173,2638174,2638175],"Iron Ore":[2638402],"Iron Trapdoor":[2638656,2638657,2638658,2638659,2638660,2638661,2638662,2638663,2638664,2638665,2638666,2638667,2638668,2638669,2638670,2638671],"Item Frame":[2638912,2638913,2638916,2638917,2638918,2638919,2638920,2638921,2638924,2638925,2638926,2638927],"Jack o'Lantern":[2647396,2647397,2647398,2647399],"Jukebox":[2639173],"Jungle Button":[2639426,2639427,2639428,2639429,2639430,2639431,2639434,2639435,2639436,2639437,2639438,2639439],"Jungle Door":[2639680,2639681,2639682,2639683,2639684,2639685,2639686,2639687,2639688,2639689,2639690,2639691,2639692,2639693,2639694,2639695,2639696,2639697,2639698,2639699,2639700,2639701,2639702,2639703,2639704,2639705,2639706,2639707,2639708,2639709,2639710,2639711],"Jungle Fence":[2639944],"Jungle Fence Gate":[2640192,2640193,2640194,2640195,2640196,2640197,2640198,2640199,2640200,2640201,2640202,2640203,2640204,2640205,2640206,2640207],"Jungle Leaves":[2640456,2640457,2640458,2640459],"Jungle Log":[2640712,2640713,2640714,2640715,2640718,2640719],"Jungle Planks":[2640972],"Jungle Pressure Plate":[2641228,2641229],"Jungle Sapling":[2641486,2641487],"Jungle Sign":[2641728,2641729,2641730,2641731,2641732,2641733,2641734,2641735,2641736,2641737,2641738,2641739,2641740,2641741,2641742,2641743],"Jungle Slab":[2642000,2642001,2642002],"Jungle Stairs":[2642256,2642257,2642258,2642259,2642260,2642261,2642262,2642263],"Jungle Trapdoor":[2642512,2642513,2642514,2642515,2642516,2642517,2642518,2642519,2642520,2642521,2642522,2642523,2642524,2642525,2642526,2642527],"Jungle Wall Sign":[2642768,2642769,2642770,2642771],"Jungle Wood":[2643024,2643025,2643028,2643029,2643030,2643031],"Krypton":[2607047],"Lab Table":[2643284,2643285,2643286,2643287],"Ladder":[2643540,2643541,2643542,2643543],"Lantern":[2643798,2643799],"Lanthanum":[2607304],"Lapis Lazuli Block":[2644056],"Lapis Lazuli Ore":[2644313],"Large Fern":[2644570,2644571],"Lava":[2644800,2644801,2644802,2644803,2644804,2644805,2644806,2644807,2644808,2644809,2644810,2644811,2644812,2644813,2644814,2644815,2644816,2644817,2644818,2644819,2644820,2644821,2644822,2644823,2644824,2644825,2644826,2644827,2644828,2644829,2644830,2644831],"Lava Cauldron":[2732208,2732209,2732210,2732211,2732212,2732213],"Lawrencium":[2607561],"Lead":[2607818],"Lectern":[2645080,2645081,2645082,2645083,2645084,2645085,2645086,2645087],"Legacy Stonecutter":[2645341],"Lever":[2645584,2645585,2645586,2645587,2645588,2645589,2645590,2645591,2645592,2645593,2645594,2645595,2645596,2645597,2645598,2645599],"Light Block":[2703680,2703681,2703682,2703683,2703684,2703685,2703686,2703687,2703688,2703689,2703690,2703691,2703692,2703693,2703694,2703695],"Lightning Rod":[2727834,2727835,2727836,2727837,2727838,2727839],"Lilac":[2646368,2646369],"Lily Pad":[2646883],"Lily of the Valley":[2646626],"Lithium":[2608075],"Livermorium":[2608332],"Loom":[2647652,2647653,2647654,2647655],"Lutetium":[2608589],"Magma Block":[2648168],"Magnesium":[2608846],"Manganese":[2609103],"Mangrove Button":[2717040,2717041,2717044,2717045,2717046,2717047,2717048,2717049,2717052,2717053,2717054,2717055],"Mangrove Door":[2718560,2718561,2718562,2718563,2718564,2718565,2718566,2718567,2718568,2718569,2718570,2718571,2718572,2718573,2718574,2718575,2718576,2718577,2718578,2718579,2718580,2718581,2718582,2718583,2718584,2718585,2718586,2718587,2718588,2718589,2718590,2718591],"Mangrove Fence":[2713190],"Mangrove Fence Gate":[2719344,2719345,2719346,2719347,2719348,2719349,2719350,2719351,2719352,2719353,2719354,2719355,2719356,2719357,2719358,2719359],"Mangrove Leaves":[2735548,2735549,2735550,2735551],"Mangrove Log":[2714728,2714729,2714732,2714733,2714734,2714735],"Mangrove Planks":[2712419],"Mangrove Pressure Plate":[2717816,2717817],"Mangrove Roots":[2733493],"Mangrove Sign":[2720896,2720897,2720898,2720899,2720900,2720901,2720902,2720903,2720904,2720905,2720906,2720907,2720908,2720909,2720910,2720911],"Mangrove Slab":[2713960,2713961,2713963],"Mangrove Stairs":[2720128,2720129,2720130,2720131,2720132,2720133,2720134,2720135],"Mangrove Trapdoor":[2716272,2716273,2716274,2716275,2716276,2716277,2716278,2716279,2716280,2716281,2716282,2716283,2716284,2716285,2716286,2716287],"Mangrove Wall Sign":[2721668,2721669,2721670,2721671],"Mangrove Wood":[2715498,2715499,2715500,2715501,2715502,2715503],"Material Reducer":[2648424,2648425,2648426,2648427],"Meitnerium":[2609360],"Melon Block":[2648682],"Melon Stem":[2648896,2648897,2648898,2648899,2648900,2648901,2648902,2648903,2648904,2648905,2648906,2648907,2648908,2648909,2648910,2648911,2648928,2648929,2648930,2648931,2648932,2648933,2648934,2648935,2648944,2648945,2648946,2648947,2648948,2648949,2648950,2648951,2648952,2648953,2648954,2648955,2648956,2648957,2648958,2648959],"Mendelevium":[2609617],"Mercury":[2609874],"Mob Head":[2649152,2649153,2649154,2649156,2649157,2649158,2649159,2649160,2649161,2649162,2649164,2649165,2649166,2649167,2649184,2649185,2649186,2649188,2649189,2649190,2649191,2649200,2649201,2649202,2649204,2649205,2649206,2649207,2649208,2649209,2649210,2649212,2649213,2649214,2649215],"Molybdenum":[2610131],"Monster Spawner":[2649453],"Moscovium":[2610388],"Mossy Cobblestone":[2649710],"Mossy Cobblestone Slab":[2649965,2649966,2649967],"Mossy Cobblestone Stairs":[2650224,2650225,2650226,2650227,2650228,2650229,2650230,2650231],"Mossy Cobblestone Wall":[2650401,2650416,2650417,2650418,2650419,2650420,2650421,2650422,2650423,2650424,2650425,2650426,2650427,2650428,2650429,2650430,2650431,2650432,2650433,2650434,2650435,2650436,2650437,2650438,2650439,2650440,2650441,2650442,2650443,2650444,2650445,2650446,2650447,2650448,2650449,2650450,2650451,2650452,2650453,2650454,2650455,2650456,2650457,2650458,2650459,2650460,2650461,2650462,2650463,2650464,2650465,2650466,2650467,2650468,2650469,2650470,2650471,2650472,2650473,2650474,2650475,2650476,2650477,2650478,2650479,2650480,2650481,2650482,2650483,2650484,2650485,2650486,2650487,2650488,2650489,2650490,2650491,2650492,2650493,2650494,2650495,2650529,2650544,2650545,2650546,2650547,2650548,2650549,2650550,2650551,2650552,2650553,2650554,2650555,2650556,2650557,2650558,2650559,2650560,2650561,2650562,2650563,2650564,2650565,2650566,2650567,2650568,2650569,2650570,2650571,2650572,2650573,2650574,2650575,2650576,2650577,2650578,2650579,2650580,2650581,2650582,2650583,2650584,2650585,2650586,2650587,2650588,2650589,2650590,2650591,2650592,2650593,2650594,2650595,2650596,2650597,2650598,2650599,2650600,2650601,2650602,2650603,2650604,2650605,2650606,2650607,2650608,2650609,2650610,2650611,2650612,2650613,2650614,2650615,2650616,2650617,2650618,2650619,2650620,2650621,2650622,2650623],"Mossy Stone Brick Slab":[2650736,2650738,2650739],"Mossy Stone Brick Stairs":[2650992,2650993,2650994,2650995,2650996,2650997,2650998,2650999],"Mossy Stone Brick Wall":[2651172,2651184,2651185,2651186,2651187,2651188,2651189,2651190,2651191,2651192,2651193,2651194,2651195,2651196,2651197,2651198,2651199,2651200,2651201,2651202,2651203,2651204,2651205,2651206,2651207,2651208,2651209,2651210,2651211,2651212,2651213,2651214,2651215,2651216,2651217,2651218,2651219,2651220,2651221,2651222,2651223,2651224,2651225,2651226,2651227,2651228,2651229,2651230,2651231,2651232,2651233,2651234,2651235,2651236,2651237,2651238,2651239,2651240,2651241,2651242,2651243,2651244,2651245,2651246,2651247,2651248,2651249,2651250,2651251,2651252,2651253,2651254,2651255,2651256,2651257,2651258,2651259,2651260,2651261,2651262,2651263,2651300,2651312,2651313,2651314,2651315,2651316,2651317,2651318,2651319,2651320,2651321,2651322,2651323,2651324,2651325,2651326,2651327,2651328,2651329,2651330,2651331,2651332,2651333,2651334,2651335,2651336,2651337,2651338,2651339,2651340,2651341,2651342,2651343,2651344,2651345,2651346,2651347,2651348,2651349,2651350,2651351,2651352,2651353,2651354,2651355,2651356,2651357,2651358,2651359,2651360,2651361,2651362,2651363,2651364,2651365,2651366,2651367,2651368,2651369,2651370,2651371,2651372,2651373,2651374,2651375,2651376,2651377,2651378,2651379,2651380,2651381,2651382,2651383,2651384,2651385,2651386,2651387,2651388,2651389,2651390,2651391],"Mossy Stone Bricks":[2651509],"Mud":[2725526],"Mud Brick Slab":[2726040,2726041,2726042],"Mud Brick Stairs":[2726296,2726297,2726298,2726299,2726300,2726301,2726302,2726303],"Mud Brick Wall":[2726400,2726401,2726402,2726403,2726404,2726405,2726406,2726407,2726408,2726409,2726410,2726411,2726412,2726413,2726414,2726415,2726416,2726417,2726418,2726419,2726420,2726421,2726422,2726423,2726424,2726425,2726426,2726427,2726428,2726429,2726430,2726431,2726432,2726433,2726434,2726435,2726436,2726437,2726438,2726439,2726440,2726441,2726442,2726443,2726444,2726445,2726446,2726447,2726448,2726449,2726450,2726451,2726452,2726453,2726454,2726455,2726456,2726457,2726458,2726459,2726460,2726461,2726462,2726463,2726474,2726480,2726481,2726482,2726483,2726484,2726485,2726486,2726487,2726488,2726489,2726490,2726491,2726492,2726493,2726494,2726495,2726528,2726529,2726530,2726531,2726532,2726533,2726534,2726535,2726536,2726537,2726538,2726539,2726540,2726541,2726542,2726543,2726544,2726545,2726546,2726547,2726548,2726549,2726550,2726551,2726552,2726553,2726554,2726555,2726556,2726557,2726558,2726559,2726560,2726561,2726562,2726563,2726564,2726565,2726566,2726567,2726568,2726569,2726570,2726571,2726572,2726573,2726574,2726575,2726576,2726577,2726578,2726579,2726580,2726581,2726582,2726583,2726584,2726585,2726586,2726587,2726588,2726589,2726590,2726591,2726602,2726608,2726609,2726610,2726611,2726612,2726613,2726614,2726615,2726616,2726617,2726618,2726619,2726620,2726621,2726622,2726623],"Mud Bricks":[2725783],"Muddy Mangrove Roots":[2733748,2733750,2733751],"Mushroom Stem":[2651766],"Mycelium":[2652023],"Neodymium":[2610645],"Neon":[2610902],"Neptunium":[2611159],"Nether Brick Fence":[2652280],"Nether Brick Slab":[2652536,2652537,2652539],"Nether Brick Stairs":[2652792,2652793,2652794,2652795,2652796,2652797,2652798,2652799],"Nether Brick Wall":[2652971,2652976,2652977,2652978,2652979,2652980,2652981,2652982,2652983,2652984,2652985,2652986,2652987,2652988,2652989,2652990,2652991,2652992,2652993,2652994,2652995,2652996,2652997,2652998,2652999,2653000,2653001,2653002,2653003,2653004,2653005,2653006,2653007,2653008,2653009,2653010,2653011,2653012,2653013,2653014,2653015,2653016,2653017,2653018,2653019,2653020,2653021,2653022,2653023,2653024,2653025,2653026,2653027,2653028,2653029,2653030,2653031,2653032,2653033,2653034,2653035,2653036,2653037,2653038,2653039,2653040,2653041,2653042,2653043,2653044,2653045,2653046,2653047,2653048,2653049,2653050,2653051,2653052,2653053,2653054,2653055,2653099,2653104,2653105,2653106,2653107,2653108,2653109,2653110,2653111,2653112,2653113,2653114,2653115,2653116,2653117,2653118,2653119,2653120,2653121,2653122,2653123,2653124,2653125,2653126,2653127,2653128,2653129,2653130,2653131,2653132,2653133,2653134,2653135,2653136,2653137,2653138,2653139,2653140,2653141,2653142,2653143,2653144,2653145,2653146,2653147,2653148,2653149,2653150,2653151,2653152,2653153,2653154,2653155,2653156,2653157,2653158,2653159,2653160,2653161,2653162,2653163,2653164,2653165,2653166,2653167,2653168,2653169,2653170,2653171,2653172,2653173,2653174,2653175,2653176,2653177,2653178,2653179,2653180,2653181,2653182,2653183],"Nether Bricks":[2653308],"Nether Gold Ore":[2725269],"Nether Portal":[2653564,2653565],"Nether Quartz Ore":[2653822],"Nether Reactor Core":[2654079],"Nether Wart":[2654336,2654337,2654338,2654339],"Nether Wart Block":[2654593],"Netherite Block":[2731180],"Netherrack":[2654850],"Nickel":[2611416],"Nihonium":[2611673],"Niobium":[2611930],"Nitrogen":[2612187],"Nobelium":[2612444],"Note Block":[2655107],"Oak Button":[2655360,2655361,2655364,2655365,2655366,2655367,2655368,2655369,2655372,2655373,2655374,2655375],"Oak Door":[2655616,2655617,2655618,2655619,2655620,2655621,2655622,2655623,2655624,2655625,2655626,2655627,2655628,2655629,2655630,2655631,2655632,2655633,2655634,2655635,2655636,2655637,2655638,2655639,2655640,2655641,2655642,2655643,2655644,2655645,2655646,2655647],"Oak Fence":[2655878],"Oak Fence Gate":[2656128,2656129,2656130,2656131,2656132,2656133,2656134,2656135,2656136,2656137,2656138,2656139,2656140,2656141,2656142,2656143],"Oak Leaves":[2656392,2656393,2656394,2656395],"Oak Log":[2656648,2656649,2656650,2656651,2656652,2656653],"Oak Planks":[2656906],"Oak Pressure Plate":[2657162,2657163],"Oak Sapling":[2657420,2657421],"Oak Sign":[2657664,2657665,2657666,2657667,2657668,2657669,2657670,2657671,2657672,2657673,2657674,2657675,2657676,2657677,2657678,2657679],"Oak Slab":[2657932,2657934,2657935],"Oak Stairs":[2658184,2658185,2658186,2658187,2658188,2658189,2658190,2658191],"Oak Trapdoor":[2658448,2658449,2658450,2658451,2658452,2658453,2658454,2658455,2658456,2658457,2658458,2658459,2658460,2658461,2658462,2658463],"Oak Wall Sign":[2658704,2658705,2658706,2658707],"Oak Wood":[2658960,2658961,2658962,2658963,2658966,2658967],"Obsidian":[2659219],"Oganesson":[2612701],"Orange Tulip":[2659733],"Osmium":[2612958],"Oxeye Daisy":[2659990],"Oxygen":[2613215],"Packed Ice":[2660247],"Packed Mud":[2726811],"Palladium":[2613472],"Peony":[2660504,2660505],"Phosphorus":[2613729],"Pink Petals":[2741712,2741713,2741714,2741715,2741716,2741717,2741718,2741719,2741720,2741721,2741722,2741723,2741724,2741725,2741726,2741727],"Pink Tulip":[2661018],"Platinum":[2613986],"Plutonium":[2614243],"Podzol":[2661275],"Polished Andesite":[2661532],"Polished Andesite Slab":[2661788,2661789,2661791],"Polished Andesite Stairs":[2662040,2662041,2662042,2662043,2662044,2662045,2662046,2662047],"Polished Basalt":[2699053,2699054,2699055],"Polished Blackstone":[2700597],"Polished Blackstone Brick Slab":[2702652,2702653,2702655],"Polished Blackstone Brick Stairs":[2702904,2702905,2702906,2702907,2702908,2702909,2702910,2702911],"Polished Blackstone Brick Wall":[2703104,2703105,2703106,2703107,2703108,2703109,2703110,2703111,2703112,2703113,2703114,2703115,2703116,2703117,2703118,2703119,2703120,2703121,2703122,2703123,2703124,2703125,2703126,2703127,2703128,2703129,2703130,2703131,2703132,2703133,2703134,2703135,2703136,2703137,2703138,2703139,2703140,2703141,2703142,2703143,2703144,2703145,2703146,2703147,2703148,2703149,2703150,2703151,2703152,2703153,2703154,2703155,2703156,2703157,2703158,2703159,2703160,2703161,2703162,2703163,2703164,2703165,2703166,2703167,2703215,2703216,2703217,2703218,2703219,2703220,2703221,2703222,2703223,2703224,2703225,2703226,2703227,2703228,2703229,2703230,2703231,2703232,2703233,2703234,2703235,2703236,2703237,2703238,2703239,2703240,2703241,2703242,2703243,2703244,2703245,2703246,2703247,2703248,2703249,2703250,2703251,2703252,2703253,2703254,2703255,2703256,2703257,2703258,2703259,2703260,2703261,2703262,2703263,2703264,2703265,2703266,2703267,2703268,2703269,2703270,2703271,2703272,2703273,2703274,2703275,2703276,2703277,2703278,2703279,2703280,2703281,2703282,2703283,2703284,2703285,2703286,2703287,2703288,2703289,2703290,2703291,2703292,2703293,2703294,2703295,2703343,2703344,2703345,2703346,2703347,2703348,2703349,2703350,2703351,2703352,2703353,2703354,2703355,2703356,2703357,2703358,2703359],"Polished Blackstone Bricks":[2702396],"Polished Blackstone Button":[2700850,2700851,2700852,2700853,2700854,2700855,2700858,2700859,2700860,2700861,2700862,2700863],"Polished Blackstone Pressure Plate":[2701110,2701111],"Polished Blackstone Slab":[2701368,2701369,2701370],"Polished Blackstone Stairs":[2701624,2701625,2701626,2701627,2701628,2701629,2701630,2701631],"Polished Blackstone Wall":[2701824,2701825,2701826,2701827,2701828,2701829,2701830,2701831,2701832,2701833,2701834,2701835,2701836,2701837,2701838,2701839,2701840,2701841,2701842,2701843,2701844,2701845,2701846,2701847,2701848,2701849,2701850,2701851,2701852,2701853,2701854,2701855,2701856,2701857,2701858,2701859,2701860,2701861,2701862,2701863,2701864,2701865,2701866,2701867,2701868,2701869,2701870,2701871,2701872,2701873,2701874,2701875,2701876,2701877,2701878,2701879,2701880,2701881,2701882,2701883,2701884,2701885,2701886,2701887,2701930,2701936,2701937,2701938,2701939,2701940,2701941,2701942,2701943,2701944,2701945,2701946,2701947,2701948,2701949,2701950,2701951,2701952,2701953,2701954,2701955,2701956,2701957,2701958,2701959,2701960,2701961,2701962,2701963,2701964,2701965,2701966,2701967,2701968,2701969,2701970,2701971,2701972,2701973,2701974,2701975,2701976,2701977,2701978,2701979,2701980,2701981,2701982,2701983,2701984,2701985,2701986,2701987,2701988,2701989,2701990,2701991,2701992,2701993,2701994,2701995,2701996,2701997,2701998,2701999,2702000,2702001,2702002,2702003,2702004,2702005,2702006,2702007,2702008,2702009,2702010,2702011,2702012,2702013,2702014,2702015,2702058,2702064,2702065,2702066,2702067,2702068,2702069,2702070,2702071,2702072,2702073,2702074,2702075,2702076,2702077,2702078,2702079],"Polished Deepslate":[2708821],"Polished Deepslate Slab":[2709076,2709078,2709079],"Polished Deepslate Stairs":[2709328,2709329,2709330,2709331,2709332,2709333,2709334,2709335],"Polished Deepslate Wall":[2709512,2709520,2709521,2709522,2709523,2709524,2709525,2709526,2709527,2709528,2709529,2709530,2709531,2709532,2709533,2709534,2709535,2709568,2709569,2709570,2709571,2709572,2709573,2709574,2709575,2709576,2709577,2709578,2709579,2709580,2709581,2709582,2709583,2709584,2709585,2709586,2709587,2709588,2709589,2709590,2709591,2709592,2709593,2709594,2709595,2709596,2709597,2709598,2709599,2709600,2709601,2709602,2709603,2709604,2709605,2709606,2709607,2709608,2709609,2709610,2709611,2709612,2709613,2709614,2709615,2709616,2709617,2709618,2709619,2709620,2709621,2709622,2709623,2709624,2709625,2709626,2709627,2709628,2709629,2709630,2709631,2709640,2709648,2709649,2709650,2709651,2709652,2709653,2709654,2709655,2709656,2709657,2709658,2709659,2709660,2709661,2709662,2709663,2709696,2709697,2709698,2709699,2709700,2709701,2709702,2709703,2709704,2709705,2709706,2709707,2709708,2709709,2709710,2709711,2709712,2709713,2709714,2709715,2709716,2709717,2709718,2709719,2709720,2709721,2709722,2709723,2709724,2709725,2709726,2709727,2709728,2709729,2709730,2709731,2709732,2709733,2709734,2709735,2709736,2709737,2709738,2709739,2709740,2709741,2709742,2709743,2709744,2709745,2709746,2709747,2709748,2709749,2709750,2709751,2709752,2709753,2709754,2709755,2709756,2709757,2709758,2709759],"Polished Diorite":[2662303],"Polished Diorite Slab":[2662560,2662561,2662562],"Polished Diorite Stairs":[2662816,2662817,2662818,2662819,2662820,2662821,2662822,2662823],"Polished Granite":[2663074],"Polished Granite Slab":[2663329,2663330,2663331],"Polished Granite Stairs":[2663584,2663585,2663586,2663587,2663588,2663589,2663590,2663591],"Polonium":[2614500],"Poppy":[2663845],"Potassium":[2614757],"Potato Block":[2664096,2664097,2664098,2664099,2664100,2664101,2664102,2664103],"Potion Cauldron":[2732464,2732465,2732466,2732467,2732468,2732469],"Powered Rail":[2664354,2664355,2664356,2664357,2664358,2664359,2664362,2664363,2664364,2664365,2664366,2664367],"Praseodymium":[2615014],"Prismarine":[2664616],"Prismarine Bricks":[2664873],"Prismarine Bricks Slab":[2665128,2665130,2665131],"Prismarine Bricks Stairs":[2665384,2665385,2665386,2665387,2665388,2665389,2665390,2665391],"Prismarine Slab":[2665644,2665645,2665646],"Prismarine Stairs":[2665896,2665897,2665898,2665899,2665900,2665901,2665902,2665903],"Prismarine Wall":[2665984,2665985,2665986,2665987,2665988,2665989,2665990,2665991,2665992,2665993,2665994,2665995,2665996,2665997,2665998,2665999,2666000,2666001,2666002,2666003,2666004,2666005,2666006,2666007,2666008,2666009,2666010,2666011,2666012,2666013,2666014,2666015,2666016,2666017,2666018,2666019,2666020,2666021,2666022,2666023,2666024,2666025,2666026,2666027,2666028,2666029,2666030,2666031,2666032,2666033,2666034,2666035,2666036,2666037,2666038,2666039,2666040,2666041,2666042,2666043,2666044,2666045,2666046,2666047,2666080,2666081,2666082,2666083,2666084,2666085,2666086,2666087,2666088,2666089,2666090,2666091,2666092,2666093,2666094,2666095,2666110,2666112,2666113,2666114,2666115,2666116,2666117,2666118,2666119,2666120,2666121,2666122,2666123,2666124,2666125,2666126,2666127,2666128,2666129,2666130,2666131,2666132,2666133,2666134,2666135,2666136,2666137,2666138,2666139,2666140,2666141,2666142,2666143,2666144,2666145,2666146,2666147,2666148,2666149,2666150,2666151,2666152,2666153,2666154,2666155,2666156,2666157,2666158,2666159,2666160,2666161,2666162,2666163,2666164,2666165,2666166,2666167,2666168,2666169,2666170,2666171,2666172,2666173,2666174,2666175,2666208,2666209,2666210,2666211,2666212,2666213,2666214,2666215,2666216,2666217,2666218,2666219,2666220,2666221,2666222,2666223,2666238],"Promethium":[2615271],"Protactinium":[2615528],"Pumpkin":[2666415],"Pumpkin Stem":[2666640,2666641,2666642,2666643,2666644,2666645,2666646,2666647,2666648,2666649,2666650,2666651,2666652,2666653,2666654,2666655,2666656,2666657,2666658,2666659,2666660,2666661,2666662,2666663,2666664,2666665,2666666,2666667,2666668,2666669,2666670,2666671,2666680,2666681,2666682,2666683,2666684,2666685,2666686,2666687],"Purple Torch":[2667184,2667185,2667187,2667190,2667191],"Purpur Block":[2667443],"Purpur Pillar":[2667700,2667701,2667702],"Purpur Slab":[2667956,2667957,2667959],"Purpur Stairs":[2668208,2668209,2668210,2668211,2668212,2668213,2668214,2668215],"Quartz Block":[2668471],"Quartz Bricks":[2709849],"Quartz Pillar":[2668728,2668729,2668730],"Quartz Slab":[2668984,2668985,2668987],"Quartz Stairs":[2669240,2669241,2669242,2669243,2669244,2669245,2669246,2669247],"Radium":[2615785],"Radon":[2616042],"Rail":[2669490,2669491,2669496,2669497,2669498,2669499,2669500,2669501,2669502,2669503],"Raw Copper Block":[2703938],"Raw Gold Block":[2704195],"Raw Iron Block":[2704452],"Red Mushroom":[2670013],"Red Mushroom Block":[2670260,2670262,2670263,2670264,2670265,2670266,2670267,2670268,2670269,2670270,2670271],"Red Nether Brick Slab":[2670525,2670526,2670527],"Red Nether Brick Stairs":[2670784,2670785,2670786,2670787,2670788,2670789,2670790,2670791],"Red Nether Brick Wall":[2670848,2670849,2670850,2670851,2670852,2670853,2670854,2670855,2670856,2670857,2670858,2670859,2670860,2670861,2670862,2670863,2670865,2670912,2670913,2670914,2670915,2670916,2670917,2670918,2670919,2670920,2670921,2670922,2670923,2670924,2670925,2670926,2670927,2670928,2670929,2670930,2670931,2670932,2670933,2670934,2670935,2670936,2670937,2670938,2670939,2670940,2670941,2670942,2670943,2670944,2670945,2670946,2670947,2670948,2670949,2670950,2670951,2670952,2670953,2670954,2670955,2670956,2670957,2670958,2670959,2670960,2670961,2670962,2670963,2670964,2670965,2670966,2670967,2670968,2670969,2670970,2670971,2670972,2670973,2670974,2670975,2670976,2670977,2670978,2670979,2670980,2670981,2670982,2670983,2670984,2670985,2670986,2670987,2670988,2670989,2670990,2670991,2670993,2671040,2671041,2671042,2671043,2671044,2671045,2671046,2671047,2671048,2671049,2671050,2671051,2671052,2671053,2671054,2671055,2671056,2671057,2671058,2671059,2671060,2671061,2671062,2671063,2671064,2671065,2671066,2671067,2671068,2671069,2671070,2671071,2671072,2671073,2671074,2671075,2671076,2671077,2671078,2671079,2671080,2671081,2671082,2671083,2671084,2671085,2671086,2671087,2671088,2671089,2671090,2671091,2671092,2671093,2671094,2671095,2671096,2671097,2671098,2671099,2671100,2671101,2671102,2671103],"Red Nether Bricks":[2671298],"Red Sand":[2671555],"Red Sandstone":[2671812],"Red Sandstone Slab":[2672068,2672069,2672071],"Red Sandstone Stairs":[2672320,2672321,2672322,2672323,2672324,2672325,2672326,2672327],"Red Sandstone Wall":[2672384,2672385,2672386,2672387,2672388,2672389,2672390,2672391,2672392,2672393,2672394,2672395,2672396,2672397,2672398,2672399,2672407,2672448,2672449,2672450,2672451,2672452,2672453,2672454,2672455,2672456,2672457,2672458,2672459,2672460,2672461,2672462,2672463,2672464,2672465,2672466,2672467,2672468,2672469,2672470,2672471,2672472,2672473,2672474,2672475,2672476,2672477,2672478,2672479,2672480,2672481,2672482,2672483,2672484,2672485,2672486,2672487,2672488,2672489,2672490,2672491,2672492,2672493,2672494,2672495,2672496,2672497,2672498,2672499,2672500,2672501,2672502,2672503,2672504,2672505,2672506,2672507,2672508,2672509,2672510,2672511,2672512,2672513,2672514,2672515,2672516,2672517,2672518,2672519,2672520,2672521,2672522,2672523,2672524,2672525,2672526,2672527,2672535,2672576,2672577,2672578,2672579,2672580,2672581,2672582,2672583,2672584,2672585,2672586,2672587,2672588,2672589,2672590,2672591,2672592,2672593,2672594,2672595,2672596,2672597,2672598,2672599,2672600,2672601,2672602,2672603,2672604,2672605,2672606,2672607,2672608,2672609,2672610,2672611,2672612,2672613,2672614,2672615,2672616,2672617,2672618,2672619,2672620,2672621,2672622,2672623,2672624,2672625,2672626,2672627,2672628,2672629,2672630,2672631,2672632,2672633,2672634,2672635,2672636,2672637,2672638,2672639],"Red Torch":[2672841,2672842,2672843,2672844,2672845],"Red Tulip":[2673097],"Redstone":[2674896,2674897,2674898,2674899,2674900,2674901,2674902,2674903,2674904,2674905,2674906,2674907,2674908,2674909,2674910,2674911],"Redstone Block":[2673354],"Redstone Comparator":[2673600,2673601,2673602,2673603,2673604,2673605,2673606,2673607,2673608,2673609,2673610,2673611,2673612,2673613,2673614,2673615],"Redstone Lamp":[2673868,2673869],"Redstone Ore":[2674124,2674125],"Redstone Repeater":[2674368,2674369,2674370,2674371,2674372,2674373,2674374,2674375,2674376,2674377,2674378,2674379,2674380,2674381,2674382,2674383,2674384,2674385,2674386,2674387,2674388,2674389,2674390,2674391,2674392,2674393,2674394,2674395,2674396,2674397,2674398,2674399],"Redstone Torch":[2674626,2674627,2674628,2674629,2674630,2674634,2674635,2674636,2674637,2674638],"Reinforced Deepslate":[2736320],"Rhenium":[2616299],"Rhodium":[2616556],"Roentgenium":[2616813],"Rose Bush":[2675410,2675411],"Rubidium":[2617070],"Ruthenium":[2617327],"Rutherfordium":[2617584],"Samarium":[2617841],"Sand":[2675667],"Sandstone":[2675924],"Sandstone Slab":[2676180,2676181,2676183],"Sandstone Stairs":[2676432,2676433,2676434,2676435,2676436,2676437,2676438,2676439],"Sandstone Wall":[2676487,2676496,2676497,2676498,2676499,2676500,2676501,2676502,2676503,2676504,2676505,2676506,2676507,2676508,2676509,2676510,2676511,2676544,2676545,2676546,2676547,2676548,2676549,2676550,2676551,2676552,2676553,2676554,2676555,2676556,2676557,2676558,2676559,2676560,2676561,2676562,2676563,2676564,2676565,2676566,2676567,2676568,2676569,2676570,2676571,2676572,2676573,2676574,2676575,2676576,2676577,2676578,2676579,2676580,2676581,2676582,2676583,2676584,2676585,2676586,2676587,2676588,2676589,2676590,2676591,2676592,2676593,2676594,2676595,2676596,2676597,2676598,2676599,2676600,2676601,2676602,2676603,2676604,2676605,2676606,2676607,2676615,2676624,2676625,2676626,2676627,2676628,2676629,2676630,2676631,2676632,2676633,2676634,2676635,2676636,2676637,2676638,2676639,2676672,2676673,2676674,2676675,2676676,2676677,2676678,2676679,2676680,2676681,2676682,2676683,2676684,2676685,2676686,2676687,2676688,2676689,2676690,2676691,2676692,2676693,2676694,2676695,2676696,2676697,2676698,2676699,2676700,2676701,2676702,2676703,2676704,2676705,2676706,2676707,2676708,2676709,2676710,2676711,2676712,2676713,2676714,2676715,2676716,2676717,2676718,2676719,2676720,2676721,2676722,2676723,2676724,2676725,2676726,2676727,2676728,2676729,2676730,2676731,2676732,2676733,2676734,2676735],"Scandium":[2618098],"Sculk":[2735035],"Sea Lantern":[2676952],"Sea Pickle":[2677208,2677209,2677210,2677211,2677212,2677213,2677214,2677215],"Seaborgium":[2618355],"Selenium":[2618612],"Shroomlight":[2712162],"Shulker Box":[2677466],"Silicon":[2618869],"Silver":[2619126],"Slime Block":[2677723],"Small Dripleaf":[2740944,2740945,2740946,2740947,2740948,2740949,2740950,2740951],"Smithing Table":[2730923],"Smoker":[2677976,2677977,2677978,2677979,2677980,2677981,2677982,2677983],"Smooth Basalt":[2699312],"Smooth Quartz Block":[2678237],"Smooth Quartz Slab":[2678492,2678494,2678495],"Smooth Quartz Stairs":[2678744,2678745,2678746,2678747,2678748,2678749,2678750,2678751],"Smooth Red Sandstone":[2679008],"Smooth Red Sandstone Slab":[2679264,2679265,2679267],"Smooth Red Sandstone Stairs":[2679520,2679521,2679522,2679523,2679524,2679525,2679526,2679527],"Smooth Sandstone":[2679779],"Smooth Sandstone Slab":[2680036,2680037,2680038],"Smooth Sandstone Stairs":[2680288,2680289,2680290,2680291,2680292,2680293,2680294,2680295],"Smooth Stone":[2680550],"Smooth Stone Slab":[2680805,2680806,2680807],"Snow Block":[2681064],"Snow Layer":[2681320,2681321,2681322,2681323,2681324,2681325,2681326,2681327],"Sodium":[2619383],"Soul Fire":[2711905],"Soul Lantern":[2711390,2711391],"Soul Sand":[2681578],"Soul Soil":[2711648],"Soul Torch":[2711130,2711131,2711132,2711133,2711135],"Sponge":[2681834,2681835],"Spore Blossom":[2731437],"Spruce Button":[2682080,2682081,2682084,2682085,2682086,2682087,2682088,2682089,2682092,2682093,2682094,2682095],"Spruce Door":[2682336,2682337,2682338,2682339,2682340,2682341,2682342,2682343,2682344,2682345,2682346,2682347,2682348,2682349,2682350,2682351,2682352,2682353,2682354,2682355,2682356,2682357,2682358,2682359,2682360,2682361,2682362,2682363,2682364,2682365,2682366,2682367],"Spruce Fence":[2682606],"Spruce Fence Gate":[2682848,2682849,2682850,2682851,2682852,2682853,2682854,2682855,2682856,2682857,2682858,2682859,2682860,2682861,2682862,2682863],"Spruce Leaves":[2683120,2683121,2683122,2683123],"Spruce Log":[2683376,2683377,2683378,2683379,2683380,2683381],"Spruce Planks":[2683634],"Spruce Pressure Plate":[2683890,2683891],"Spruce Sapling":[2684148,2684149],"Spruce Sign":[2684400,2684401,2684402,2684403,2684404,2684405,2684406,2684407,2684408,2684409,2684410,2684411,2684412,2684413,2684414,2684415],"Spruce Slab":[2684660,2684662,2684663],"Spruce Stairs":[2684912,2684913,2684914,2684915,2684916,2684917,2684918,2684919],"Spruce Trapdoor":[2685168,2685169,2685170,2685171,2685172,2685173,2685174,2685175,2685176,2685177,2685178,2685179,2685180,2685181,2685182,2685183],"Spruce Wall Sign":[2685432,2685433,2685434,2685435],"Spruce Wood":[2685688,2685689,2685690,2685691,2685694,2685695],"Stained Clay":[2685936,2685937,2685938,2685939,2685940,2685941,2685942,2685943,2685944,2685945,2685946,2685947,2685948,2685949,2685950,2685951],"Stained Glass":[2686192,2686193,2686194,2686195,2686196,2686197,2686198,2686199,2686200,2686201,2686202,2686203,2686204,2686205,2686206,2686207],"Stained Glass Pane":[2686448,2686449,2686450,2686451,2686452,2686453,2686454,2686455,2686456,2686457,2686458,2686459,2686460,2686461,2686462,2686463],"Stained Hardened Glass":[2686704,2686705,2686706,2686707,2686708,2686709,2686710,2686711,2686712,2686713,2686714,2686715,2686716,2686717,2686718,2686719],"Stained Hardened Glass Pane":[2686960,2686961,2686962,2686963,2686964,2686965,2686966,2686967,2686968,2686969,2686970,2686971,2686972,2686973,2686974,2686975],"Stone":[2686976],"Stone Brick Slab":[2687232,2687233,2687235],"Stone Brick Stairs":[2687488,2687489,2687490,2687491,2687492,2687493,2687494,2687495],"Stone Brick Wall":[2687744,2687745,2687746,2687747,2687748,2687749,2687750,2687751,2687752,2687753,2687754,2687755,2687756,2687757,2687758,2687759,2687760,2687761,2687762,2687763,2687764,2687765,2687766,2687767,2687768,2687769,2687770,2687771,2687772,2687773,2687774,2687775,2687776,2687777,2687778,2687779,2687780,2687781,2687782,2687783,2687784,2687785,2687786,2687787,2687788,2687789,2687790,2687791,2687792,2687793,2687794,2687795,2687796,2687797,2687798,2687799,2687800,2687801,2687802,2687803,2687804,2687805,2687806,2687807,2687808,2687809,2687810,2687811,2687812,2687813,2687814,2687815,2687816,2687817,2687818,2687819,2687820,2687821,2687822,2687823,2687827,2687872,2687873,2687874,2687875,2687876,2687877,2687878,2687879,2687880,2687881,2687882,2687883,2687884,2687885,2687886,2687887,2687888,2687889,2687890,2687891,2687892,2687893,2687894,2687895,2687896,2687897,2687898,2687899,2687900,2687901,2687902,2687903,2687904,2687905,2687906,2687907,2687908,2687909,2687910,2687911,2687912,2687913,2687914,2687915,2687916,2687917,2687918,2687919,2687920,2687921,2687922,2687923,2687924,2687925,2687926,2687927,2687928,2687929,2687930,2687931,2687932,2687933,2687934,2687935,2687936,2687937,2687938,2687939,2687940,2687941,2687942,2687943,2687944,2687945,2687946,2687947,2687948,2687949,2687950,2687951,2687955],"Stone Bricks":[2688004],"Stone Button":[2688256,2688257,2688260,2688261,2688262,2688263,2688264,2688265,2688268,2688269,2688270,2688271],"Stone Pressure Plate":[2688518,2688519],"Stone Slab":[2688773,2688774,2688775],"Stone Stairs":[2689032,2689033,2689034,2689035,2689036,2689037,2689038,2689039],"Stonecutter":[2689288,2689289,2689290,2689291],"Strontium":[2619640],"Sugarcane":[2692624,2692625,2692626,2692627,2692628,2692629,2692630,2692631,2692632,2692633,2692634,2692635,2692636,2692637,2692638,2692639],"Sulfur":[2619897],"Sunflower":[2692886,2692887],"Sweet Berry Bush":[2693144,2693145,2693146,2693147],"TNT":[2693656,2693657,2693658,2693659],"Tall Grass":[2693401],"Tantalum":[2620154],"Technetium":[2620411],"Tellurium":[2620668],"Tennessine":[2620925],"Terbium":[2621182],"Thallium":[2621439],"Thorium":[2621440],"Thulium":[2621697],"Tin":[2621954],"Tinted Glass":[2722442],"Titanium":[2622211],"Torch":[2693912,2693913,2693914,2693918,2693919],"Trapped Chest":[2694172,2694173,2694174,2694175],"Tripwire":[2694416,2694417,2694418,2694419,2694420,2694421,2694422,2694423,2694424,2694425,2694426,2694427,2694428,2694429,2694430,2694431],"Tripwire Hook":[2694672,2694673,2694674,2694675,2694676,2694677,2694678,2694679,2694680,2694681,2694682,2694683,2694684,2694685,2694686,2694687],"Tuff":[2710877],"Tungsten":[2622468],"Twisting Vines":[2734240,2734241,2734248,2734249,2734250,2734251,2734252,2734253,2734254,2734255,2734256,2734257,2734258,2734259,2734260,2734261,2734262,2734263,2734264,2734265,2734266,2734267,2734268,2734269,2734270,2734271],"Underwater Torch":[2694938,2694939,2694940,2694941,2694942],"Uranium":[2622725],"Vanadium":[2622982],"Vines":[2695200,2695201,2695202,2695203,2695204,2695205,2695206,2695207,2695208,2695209,2695210,2695211,2695212,2695213,2695214,2695215],"Wall Banner":[2695424,2695425,2695426,2695427,2695428,2695429,2695430,2695431,2695432,2695433,2695434,2695435,2695436,2695437,2695438,2695439,2695440,2695441,2695442,2695443,2695444,2695445,2695446,2695447,2695448,2695449,2695450,2695451,2695452,2695453,2695454,2695455,2695456,2695457,2695458,2695459,2695460,2695461,2695462,2695463,2695464,2695465,2695466,2695467,2695468,2695469,2695470,2695471,2695472,2695473,2695474,2695475,2695476,2695477,2695478,2695479,2695480,2695481,2695482,2695483,2695484,2695485,2695486,2695487],"Wall Coral Fan":[2695680,2695681,2695682,2695683,2695686,2695688,2695689,2695690,2695691,2695694,2695696,2695697,2695698,2695699,2695702,2695704,2695705,2695706,2695707,2695710,2695712,2695713,2695714,2695715,2695718,2695720,2695721,2695722,2695723,2695726,2695728,2695729,2695730,2695731,2695734,2695736,2695737,2695738,2695739,2695742],"Warped Button":[2717554,2717555,2717556,2717557,2717558,2717559,2717562,2717563,2717564,2717565,2717566,2717567],"Warped Door":[2719072,2719073,2719074,2719075,2719076,2719077,2719078,2719079,2719080,2719081,2719082,2719083,2719084,2719085,2719086,2719087,2719088,2719089,2719090,2719091,2719092,2719093,2719094,2719095,2719096,2719097,2719098,2719099,2719100,2719101,2719102,2719103],"Warped Fence":[2713704],"Warped Fence Gate":[2719872,2719873,2719874,2719875,2719876,2719877,2719878,2719879,2719880,2719881,2719882,2719883,2719884,2719885,2719886,2719887],"Warped Hyphae":[2716016,2716017,2716018,2716019,2716020,2716021],"Warped Planks":[2712933],"Warped Pressure Plate":[2718330,2718331],"Warped Sign":[2721408,2721409,2721410,2721411,2721412,2721413,2721414,2721415,2721416,2721417,2721418,2721419,2721420,2721421,2721422,2721423],"Warped Slab":[2714473,2714474,2714475],"Warped Stairs":[2720640,2720641,2720642,2720643,2720644,2720645,2720646,2720647],"Warped Stem":[2715242,2715243,2715244,2715245,2715246,2715247],"Warped Trapdoor":[2716784,2716785,2716786,2716787,2716788,2716789,2716790,2716791,2716792,2716793,2716794,2716795,2716796,2716797,2716798,2716799],"Warped Wall Sign":[2722184,2722185,2722186,2722187],"Warped Wart Block":[2727068],"Water":[2695968,2695969,2695970,2695971,2695972,2695973,2695974,2695975,2695976,2695977,2695978,2695979,2695980,2695981,2695982,2695983,2695984,2695985,2695986,2695987,2695988,2695989,2695990,2695991,2695992,2695993,2695994,2695995,2695996,2695997,2695998,2695999],"Water Cauldron":[2731946,2731947,2731948,2731949,2731950,2731951],"Weeping Vines":[2734496,2734497,2734504,2734505,2734506,2734507,2734508,2734509,2734510,2734511,2734512,2734513,2734514,2734515,2734516,2734517,2734518,2734519,2734520,2734521,2734522,2734523,2734524,2734525,2734526,2734527],"Weighted Pressure Plate Heavy":[2696224,2696225,2696226,2696227,2696228,2696229,2696230,2696231,2696232,2696233,2696234,2696235,2696236,2696237,2696238,2696239],"Weighted Pressure Plate Light":[2696480,2696481,2696482,2696483,2696484,2696485,2696486,2696487,2696488,2696489,2696490,2696491,2696492,2696493,2696494,2696495],"Wheat Block":[2696736,2696737,2696738,2696739,2696740,2696741,2696742,2696743],"White Tulip":[2697256],"Wither Rose":[2730152],"Wool":[2697504,2697505,2697506,2697507,2697508,2697509,2697510,2697511,2697512,2697513,2697514,2697515,2697516,2697517,2697518,2697519],"Xenon":[2623239],"Ytterbium":[2623496],"Yttrium":[2623753],"Zinc":[2624267],"Zirconium":[2624524],"ate!upd":[2637117],"reserved6":[2675153],"update!":[2636860]},"stateDataBits":8} \ No newline at end of file From 59c88fe7f714abb88f2d472bf41d15478d43d4e7 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 9 Aug 2023 12:21:09 +0100 Subject: [PATCH 10/46] Added WorldDifficultyChangeEvent --- .../world/WorldDifficultyChangeEvent.php | 44 +++++++++++++++++++ src/world/World.php | 2 + 2 files changed, 46 insertions(+) create mode 100644 src/event/world/WorldDifficultyChangeEvent.php diff --git a/src/event/world/WorldDifficultyChangeEvent.php b/src/event/world/WorldDifficultyChangeEvent.php new file mode 100644 index 000000000..90cee376a --- /dev/null +++ b/src/event/world/WorldDifficultyChangeEvent.php @@ -0,0 +1,44 @@ +oldDifficulty; } + + public function getNewDifficulty() : int{ return $this->newDifficulty; } +} diff --git a/src/world/World.php b/src/world/World.php index 7d9311c70..934ff6690 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -52,6 +52,7 @@ use pocketmine\event\world\ChunkLoadEvent; use pocketmine\event\world\ChunkPopulateEvent; use pocketmine\event\world\ChunkUnloadEvent; use pocketmine\event\world\SpawnChangeEvent; +use pocketmine\event\world\WorldDifficultyChangeEvent; use pocketmine\event\world\WorldDisplayNameChangeEvent; use pocketmine\event\world\WorldParticleEvent; use pocketmine\event\world\WorldSaveEvent; @@ -3127,6 +3128,7 @@ class World implements ChunkManager{ if($difficulty < 0 || $difficulty > 3){ throw new \InvalidArgumentException("Invalid difficulty level $difficulty"); } + (new WorldDifficultyChangeEvent($this, $this->getDifficulty(), $difficulty))->call(); $this->provider->getWorldData()->setDifficulty($difficulty); foreach($this->players as $player){ From a5d8ef7a6c5ad93a385de8d3be92eef29e812494 Mon Sep 17 00:00:00 2001 From: Hugo_ <55756021+Dhaiven@users.noreply.github.com> Date: Wed, 9 Aug 2023 13:33:25 +0200 Subject: [PATCH 11/46] Add FarmlandHydrationChangeEvent (#5916) --- src/block/Farmland.php | 17 ++++-- .../block/FarmlandHydrationChangeEvent.php | 59 +++++++++++++++++++ 2 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 src/event/block/FarmlandHydrationChangeEvent.php diff --git a/src/block/Farmland.php b/src/block/Farmland.php index 6fcdcd8bc..2dd795eec 100644 --- a/src/block/Farmland.php +++ b/src/block/Farmland.php @@ -26,6 +26,7 @@ namespace pocketmine\block; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\entity\Entity; use pocketmine\entity\Living; +use pocketmine\event\block\FarmlandHydrationChangeEvent; use pocketmine\event\entity\EntityTrampleFarmlandEvent; use pocketmine\item\Item; use pocketmine\math\AxisAlignedBB; @@ -73,14 +74,22 @@ class Farmland extends Transparent{ $world = $this->position->getWorld(); if(!$this->canHydrate()){ if($this->wetness > 0){ - $this->wetness--; - $world->setBlock($this->position, $this, false); + $event = new FarmlandHydrationChangeEvent($this, $this->wetness, $this->wetness - 1); + $event->call(); + if(!$event->isCancelled()){ + $this->wetness = $event->getNewHydration(); + $world->setBlock($this->position, $this, false); + } }else{ $world->setBlock($this->position, VanillaBlocks::DIRT()); } }elseif($this->wetness < self::MAX_WETNESS){ - $this->wetness = self::MAX_WETNESS; - $world->setBlock($this->position, $this, false); + $event = new FarmlandHydrationChangeEvent($this, $this->wetness, self::MAX_WETNESS); + $event->call(); + if(!$event->isCancelled()){ + $this->wetness = $event->getNewHydration(); + $world->setBlock($this->position, $this, false); + } } } diff --git a/src/event/block/FarmlandHydrationChangeEvent.php b/src/event/block/FarmlandHydrationChangeEvent.php new file mode 100644 index 000000000..4df5c1a4e --- /dev/null +++ b/src/event/block/FarmlandHydrationChangeEvent.php @@ -0,0 +1,59 @@ +oldHydration; + } + + public function getNewHydration() : int{ + return $this->newHydration; + } + + public function setNewHydration(int $hydration) : void{ + if($hydration < 0 || $hydration > Farmland::MAX_WETNESS){ + throw new \InvalidArgumentException("Hydration must be in range 0 ... " . Farmland::MAX_WETNESS); + } + $this->newHydration = $hydration; + } +} From 447f061566c733272cf3653f29215e066678f18f Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 9 Aug 2023 15:45:15 +0100 Subject: [PATCH 12/46] Use Event::hasHandlers() for a few more hot events --- src/block/Fire.php | 10 +++++++--- src/block/Leaves.php | 11 ++++++++--- src/world/World.php | 15 +++++++++------ 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/block/Fire.php b/src/block/Fire.php index a39ef6b9c..11378b82b 100644 --- a/src/block/Fire.php +++ b/src/block/Fire.php @@ -145,9 +145,13 @@ class Fire extends BaseFire{ private function burnBlock(Block $block, int $chanceBound) : void{ if(mt_rand(0, $chanceBound) < $block->getFlammability()){ - $ev = new BlockBurnEvent($block, $this); - $ev->call(); - if(!$ev->isCancelled()){ + $cancelled = false; + if(BlockBurnEvent::hasHandlers()){ + $ev = new BlockBurnEvent($block, $this); + $ev->call(); + $cancelled = $ev->isCancelled(); + } + if(!$cancelled){ $block->onIncinerate(); $world = $this->position->getWorld(); diff --git a/src/block/Leaves.php b/src/block/Leaves.php index 235190454..b1839dca6 100644 --- a/src/block/Leaves.php +++ b/src/block/Leaves.php @@ -116,10 +116,15 @@ class Leaves extends Transparent{ public function onRandomTick() : void{ if(!$this->noDecay && $this->checkDecay){ - $ev = new LeavesDecayEvent($this); - $ev->call(); + $cancelled = false; + if(LeavesDecayEvent::hasHandlers()){ + $ev = new LeavesDecayEvent($this); + $ev->call(); + $cancelled = $ev->isCancelled(); + } + $world = $this->position->getWorld(); - if($ev->isCancelled() || $this->findLog($this->position)){ + if($cancelled || $this->findLog($this->position)){ $this->checkDecay = false; $world->setBlock($this->position, $this, false); }else{ diff --git a/src/world/World.php b/src/world/World.php index 934ff6690..3ff5632cb 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -972,14 +972,17 @@ class World implements ChunkManager{ $block = $replacement; } - $ev = new BlockUpdateEvent($block); - $ev->call(); - if(!$ev->isCancelled()){ - foreach($this->getNearbyEntities(AxisAlignedBB::one()->offset($x, $y, $z)) as $entity){ - $entity->onNearbyBlockChange(); + if(BlockUpdateEvent::hasHandlers()){ + $ev = new BlockUpdateEvent($block); + $ev->call(); + if($ev->isCancelled()){ + continue; } - $block->onNearbyBlockChange(); } + foreach($this->getNearbyEntities(AxisAlignedBB::one()->offset($x, $y, $z)) as $entity){ + $entity->onNearbyBlockChange(); + } + $block->onNearbyBlockChange(); } $this->timings->scheduledBlockUpdates->stopTiming(); From 97700636c6692f65afd2f4f91e3bede7fe7ed45b Mon Sep 17 00:00:00 2001 From: Dylan T Date: Wed, 9 Aug 2023 16:09:16 +0100 Subject: [PATCH 13/46] PluginBase: added getResourceFolder() and getResourcePath(), deprecate getResource() (#5961) This is a step towards #5958. While it's not actually necessary to add these functions (since people could just use getFile() . "/resources/whatever.yml" instead), this helps preserve the convention of using the `resources` folder, which might be helpful for external tools. As an example: stream_get_contents($this->getResource("lang/eng.ini")); (which is actually incorrect, since it leaks a resource) can now be replaced by file_get_contents($this->getResourcePath("lang/eng.ini")); quite trivially. getResourceFolder() can be used with scandir() to enumerate resources instead of using getResources(), although getResources() still provides utility in the relativized resource paths. --- src/plugin/PluginBase.php | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/plugin/PluginBase.php b/src/plugin/PluginBase.php index c8f3e1c39..e4270de00 100644 --- a/src/plugin/PluginBase.php +++ b/src/plugin/PluginBase.php @@ -50,6 +50,8 @@ use const DIRECTORY_SEPARATOR; abstract class PluginBase implements Plugin, CommandExecutor{ private bool $isEnabled = false; + private string $resourceFolder; + private ?Config $config = null; private string $configFile; @@ -67,6 +69,8 @@ abstract class PluginBase implements Plugin, CommandExecutor{ $this->dataFolder = rtrim($dataFolder, "/" . DIRECTORY_SEPARATOR) . "/"; //TODO: this is accessed externally via reflection, not unused $this->file = rtrim($file, "/" . DIRECTORY_SEPARATOR) . "/"; + $this->resourceFolder = Path::join($this->file, "resources") . "/"; + $this->configFile = Path::join($this->dataFolder, "config.yml"); $prefix = $this->getDescription()->getPrefix(); @@ -209,6 +213,27 @@ abstract class PluginBase implements Plugin, CommandExecutor{ } /** + * Returns the path to the folder where the plugin's embedded resource files are usually located. + * Note: This is NOT the same as the data folder. The files in this folder should be considered read-only. + */ + public function getResourceFolder() : string{ + return $this->resourceFolder; + } + + /** + * Returns the full path to a data file in the plugin's resources folder. + * This path can be used with standard PHP functions like fopen() or file_get_contents(). + * + * Note: Any path returned by this function should be considered READ-ONLY. + */ + public function getResourcePath(string $filename) : string{ + return Path::join($this->getResourceFolder(), $filename); + } + + /** + * @deprecated Prefer using standard PHP functions with {@link PluginBase::getResourcePath()}, like + * file_get_contents() or fopen(). + * * Gets an embedded resource on the plugin file. * WARNING: You must close the resource given using fclose() * From 37f2dafae1701bc840f5a57185a6b38f0cf8b72c Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 9 Aug 2023 16:16:11 +0100 Subject: [PATCH 14/46] PluginBase: make saveResource() use copy() instead of overengineered streams garbage --- src/plugin/PluginBase.php | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/plugin/PluginBase.php b/src/plugin/PluginBase.php index 6ef4748cf..1b537a11c 100644 --- a/src/plugin/PluginBase.php +++ b/src/plugin/PluginBase.php @@ -30,19 +30,16 @@ use pocketmine\command\PluginCommand; use pocketmine\lang\KnownTranslationFactory; use pocketmine\scheduler\TaskScheduler; use pocketmine\Server; -use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Config; use pocketmine\utils\Utils; use Symfony\Component\Filesystem\Path; +use function copy; use function count; use function dirname; -use function fclose; use function file_exists; -use function fopen; use function mkdir; use function rtrim; use function str_contains; -use function stream_copy_to_stream; use function strtolower; use function trim; use const DIRECTORY_SEPARATOR; @@ -251,26 +248,21 @@ abstract class PluginBase implements Plugin, CommandExecutor{ return false; } - $out = Path::join($this->dataFolder, $filename); - if(file_exists($out) && !$replace){ + $source = Path::join($this->resourceFolder, $filename); + if(!file_exists($source)){ return false; } - if(($resource = $this->getResource($filename)) === null){ + $destination = Path::join($this->dataFolder, $filename); + if(file_exists($destination) && !$replace){ return false; } - if(!file_exists(dirname($out))){ - mkdir(dirname($out), 0755, true); + if(!file_exists(dirname($destination))){ + mkdir(dirname($destination), 0755, true); } - $fp = fopen($out, "wb"); - if($fp === false) throw new AssumptionFailedError("fopen() should not fail with wb flags"); - - $ret = stream_copy_to_stream($resource, $fp) > 0; - fclose($fp); - fclose($resource); - return $ret; + return copy($source, $destination); } /** From e48b5b2ec0891b0c360d562c54bebd14fa55c86c Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 10 Aug 2023 13:07:03 +0100 Subject: [PATCH 15/46] GeneratorManager: allow aliasing existing generators --- src/world/generator/GeneratorManager.php | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/world/generator/GeneratorManager.php b/src/world/generator/GeneratorManager.php index 180450b72..291ea91de 100644 --- a/src/world/generator/GeneratorManager.php +++ b/src/world/generator/GeneratorManager.php @@ -52,9 +52,9 @@ final class GeneratorManager{ } }); $this->addGenerator(Normal::class, "normal", fn() => null); - $this->addGenerator(Normal::class, "default", fn() => null); - $this->addGenerator(Nether::class, "hell", fn() => null); + $this->addAlias("normal", "default"); $this->addGenerator(Nether::class, "nether", fn() => null); + $this->addAlias("nether", "hell"); } /** @@ -80,6 +80,22 @@ final class GeneratorManager{ $this->list[$name] = new GeneratorManagerEntry($class, $presetValidator); } + /** + * Aliases an already-registered generator name to another name. Useful if you want to map a generator name to an + * existing generator without having to replicate the parameters. + */ + public function addAlias(string $name, string $alias) : void{ + $name = strtolower($name); + $alias = strtolower($alias); + if(!isset($this->list[$name])){ + throw new \InvalidArgumentException("Alias \"$name\" is not assigned"); + } + if(isset($this->list[$alias])){ + throw new \InvalidArgumentException("Alias \"$alias\" is already assigned"); + } + $this->list[$alias] = $this->list[$name]; + } + /** * Returns a list of names for registered generators. * From 39867b97c5026cc8d09724eec51f85216c98763b Mon Sep 17 00:00:00 2001 From: S3v3Nice <49648416+S3v3Nice@users.noreply.github.com> Date: Tue, 15 Aug 2023 19:28:26 +0300 Subject: [PATCH 16/46] Implement enchanting using enchanting tables (#5953) Co-authored-by: Dylan K. Taylor --- src/block/Block.php | 16 ++ src/block/BlockTypeInfo.php | 17 +- src/block/VanillaBlocks.php | 5 +- src/block/inventory/EnchantInventory.php | 45 ++++ .../ItemSerializerDeserializerRegistrar.php | 1 + src/entity/Human.php | 16 +- .../PlayerEnchantOptionsRequestEvent.php | 75 ++++++ src/event/player/PlayerItemEnchantEvent.php | 85 ++++++ .../transaction/EnchantTransaction.php | 132 ++++++++++ src/item/Armor.php | 15 +- src/item/ArmorMaterial.php | 42 +++ src/item/ArmorTypeInfo.php | 13 +- src/item/EnchantedBook.php | 30 +++ src/item/Item.php | 28 +- src/item/ItemBlock.php | 2 +- src/item/ItemTypeIds.php | 3 +- src/item/StringToItemParser.php | 1 + src/item/TieredTool.php | 11 +- src/item/ToolTier.php | 25 +- src/item/VanillaArmorMaterials.php | 73 ++++++ src/item/VanillaItems.php | 129 ++++----- .../AvailableEnchantmentRegistry.php | 211 +++++++++++++++ src/item/enchantment/EnchantOption.php | 66 +++++ src/item/enchantment/Enchantment.php | 66 ++++- src/item/enchantment/EnchantmentHelper.php | 217 +++++++++++++++ .../IncompatibleEnchantmentGroups.php | 34 +++ .../IncompatibleEnchantmentRegistry.php | 94 +++++++ .../ItemEnchantmentTagRegistry.php | 190 ++++++++++++++ src/item/enchantment/ItemEnchantmentTags.php | 57 ++++ src/item/enchantment/ItemFlags.php | 3 +- .../enchantment/ProtectionEnchantment.php | 9 +- src/item/enchantment/VanillaEnchantments.php | 247 +++++++++++++++--- src/network/mcpe/InventoryManager.php | 47 ++++ .../mcpe/handler/ItemStackRequestExecutor.php | 15 +- 34 files changed, 1892 insertions(+), 128 deletions(-) create mode 100644 src/event/player/PlayerEnchantOptionsRequestEvent.php create mode 100644 src/event/player/PlayerItemEnchantEvent.php create mode 100644 src/inventory/transaction/EnchantTransaction.php create mode 100644 src/item/ArmorMaterial.php create mode 100644 src/item/EnchantedBook.php create mode 100644 src/item/VanillaArmorMaterials.php create mode 100644 src/item/enchantment/AvailableEnchantmentRegistry.php create mode 100644 src/item/enchantment/EnchantOption.php create mode 100644 src/item/enchantment/EnchantmentHelper.php create mode 100644 src/item/enchantment/IncompatibleEnchantmentGroups.php create mode 100644 src/item/enchantment/IncompatibleEnchantmentRegistry.php create mode 100644 src/item/enchantment/ItemEnchantmentTagRegistry.php create mode 100644 src/item/enchantment/ItemEnchantmentTags.php diff --git a/src/block/Block.php b/src/block/Block.php index 0e045792f..9f61982ff 100644 --- a/src/block/Block.php +++ b/src/block/Block.php @@ -36,6 +36,9 @@ use pocketmine\data\runtime\RuntimeDataSizeCalculator; use pocketmine\data\runtime\RuntimeDataWriter; use pocketmine\entity\Entity; use pocketmine\entity\projectile\Projectile; +use pocketmine\item\enchantment\AvailableEnchantmentRegistry; +use pocketmine\item\enchantment\ItemEnchantmentTagRegistry; +use pocketmine\item\enchantment\ItemEnchantmentTags; use pocketmine\item\enchantment\VanillaEnchantments; use pocketmine\item\Item; use pocketmine\item\ItemBlock; @@ -422,6 +425,19 @@ class Block{ return $this->typeInfo->getBreakInfo(); } + /** + * Returns tags that represent the type of item being enchanted and are used to determine + * what enchantments can be applied to the item of this block during in-game enchanting (enchanting table, anvil, fishing, etc.). + * @see ItemEnchantmentTags + * @see ItemEnchantmentTagRegistry + * @see AvailableEnchantmentRegistry + * + * @return string[] + */ + public function getEnchantmentTags() : array{ + return $this->typeInfo->getEnchantmentTags(); + } + /** * Do the actions needed so the block is broken with the Item * diff --git a/src/block/BlockTypeInfo.php b/src/block/BlockTypeInfo.php index eb6b89ad1..a1424ef3c 100644 --- a/src/block/BlockTypeInfo.php +++ b/src/block/BlockTypeInfo.php @@ -35,10 +35,12 @@ final class BlockTypeInfo{ /** * @param string[] $typeTags + * @param string[] $enchantmentTags */ public function __construct( private BlockBreakInfo $breakInfo, - array $typeTags = [] + array $typeTags = [], + private array $enchantmentTags = [] ){ $this->typeTags = array_fill_keys($typeTags, true); } @@ -49,4 +51,17 @@ final class BlockTypeInfo{ public function getTypeTags() : array{ return array_keys($this->typeTags); } public function hasTypeTag(string $tag) : bool{ return isset($this->typeTags[$tag]); } + + /** + * Returns tags that represent the type of item being enchanted and are used to determine + * what enchantments can be applied to the item of this block during in-game enchanting (enchanting table, anvil, fishing, etc.). + * @see ItemEnchantmentTags + * @see ItemEnchantmentTagRegistry + * @see AvailableEnchantmentRegistry + * + * @return string[] + */ + public function getEnchantmentTags() : array{ + return $this->enchantmentTags; + } } diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index d733b06c2..cb612031f 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -59,6 +59,7 @@ use pocketmine\block\utils\SaplingType; use pocketmine\block\utils\WoodType; use pocketmine\crafting\FurnaceType; use pocketmine\entity\projectile\Projectile; +use pocketmine\item\enchantment\ItemEnchantmentTags as EnchantmentTags; use pocketmine\item\Item; use pocketmine\item\ToolTier; use pocketmine\math\Facing; @@ -966,7 +967,7 @@ final class VanillaBlocks{ $pumpkinBreakInfo = new Info(BreakInfo::axe(1.0)); self::register("pumpkin", new Pumpkin(new BID(Ids::PUMPKIN), "Pumpkin", $pumpkinBreakInfo)); - self::register("carved_pumpkin", new CarvedPumpkin(new BID(Ids::CARVED_PUMPKIN), "Carved Pumpkin", $pumpkinBreakInfo)); + self::register("carved_pumpkin", new CarvedPumpkin(new BID(Ids::CARVED_PUMPKIN), "Carved Pumpkin", new Info(BreakInfo::axe(1.0), enchantmentTags: [EnchantmentTags::MASK]))); self::register("lit_pumpkin", new LitPumpkin(new BID(Ids::LIT_PUMPKIN), "Jack o'Lantern", $pumpkinBreakInfo)); self::register("pumpkin_stem", new PumpkinStem(new BID(Ids::PUMPKIN_STEM), "Pumpkin Stem", new Info(BreakInfo::instant()))); @@ -1002,7 +1003,7 @@ final class VanillaBlocks{ self::register("sea_lantern", new SeaLantern(new BID(Ids::SEA_LANTERN), "Sea Lantern", new Info(new BreakInfo(0.3)))); self::register("sea_pickle", new SeaPickle(new BID(Ids::SEA_PICKLE), "Sea Pickle", new Info(BreakInfo::instant()))); - self::register("mob_head", new MobHead(new BID(Ids::MOB_HEAD, TileMobHead::class), "Mob Head", new Info(new BreakInfo(1.0)))); + self::register("mob_head", new MobHead(new BID(Ids::MOB_HEAD, TileMobHead::class), "Mob Head", new Info(new BreakInfo(1.0), enchantmentTags: [EnchantmentTags::MASK]))); self::register("slime", new Slime(new BID(Ids::SLIME), "Slime Block", new Info(BreakInfo::instant()))); self::register("snow", new Snow(new BID(Ids::SNOW), "Snow Block", new Info(BreakInfo::shovel(0.2, ToolTier::WOOD())))); self::register("snow_layer", new SnowLayer(new BID(Ids::SNOW_LAYER), "Snow Layer", new Info(BreakInfo::shovel(0.1, ToolTier::WOOD())))); diff --git a/src/block/inventory/EnchantInventory.php b/src/block/inventory/EnchantInventory.php index 2c682d134..5d7e45259 100644 --- a/src/block/inventory/EnchantInventory.php +++ b/src/block/inventory/EnchantInventory.php @@ -23,9 +23,15 @@ declare(strict_types=1); namespace pocketmine\block\inventory; +use pocketmine\event\player\PlayerEnchantOptionsRequestEvent; use pocketmine\inventory\SimpleInventory; use pocketmine\inventory\TemporaryInventory; +use pocketmine\item\enchantment\EnchantmentHelper as Helper; +use pocketmine\item\enchantment\EnchantOption; +use pocketmine\item\Item; use pocketmine\world\Position; +use function array_values; +use function count; class EnchantInventory extends SimpleInventory implements BlockInventory, TemporaryInventory{ use BlockInventoryTrait; @@ -33,8 +39,47 @@ class EnchantInventory extends SimpleInventory implements BlockInventory, Tempor public const SLOT_INPUT = 0; public const SLOT_LAPIS = 1; + /** @var EnchantOption[] $options */ + private array $options = []; + public function __construct(Position $holder){ $this->holder = $holder; parent::__construct(2); } + + protected function onSlotChange(int $index, Item $before) : void{ + if($index === self::SLOT_INPUT){ + foreach($this->viewers as $viewer){ + $this->options = []; + $item = $this->getInput(); + $options = Helper::getEnchantOptions($this->holder, $item, $viewer->getEnchantmentSeed()); + + $event = new PlayerEnchantOptionsRequestEvent($viewer, $this, $options); + $event->call(); + if(!$event->isCancelled() && count($event->getOptions()) > 0){ + $this->options = array_values($event->getOptions()); + $viewer->getNetworkSession()->getInvManager()?->syncEnchantingTableOptions($this->options); + } + } + } + + parent::onSlotChange($index, $before); + } + + public function getInput() : Item{ + return $this->getItem(self::SLOT_INPUT); + } + + public function getLapis() : Item{ + return $this->getItem(self::SLOT_LAPIS); + } + + public function getOutput(int $optionId) : ?Item{ + $option = $this->getOption($optionId); + return $option === null ? null : Helper::enchantItem($this->getInput(), $option->getEnchantments()); + } + + public function getOption(int $optionId) : ?EnchantOption{ + return $this->options[$optionId] ?? null; + } } diff --git a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php index 6f557ddb1..ccf430b9c 100644 --- a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php +++ b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php @@ -223,6 +223,7 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Item(Ids::ECHO_SHARD, Items::ECHO_SHARD()); $this->map1to1Item(Ids::EGG, Items::EGG()); $this->map1to1Item(Ids::EMERALD, Items::EMERALD()); + $this->map1to1Item(Ids::ENCHANTED_BOOK, Items::ENCHANTED_BOOK()); $this->map1to1Item(Ids::ENCHANTED_GOLDEN_APPLE, Items::ENCHANTED_GOLDEN_APPLE()); $this->map1to1Item(Ids::ENDER_PEARL, Items::ENDER_PEARL()); $this->map1to1Item(Ids::EXPERIENCE_BOTTLE, Items::EXPERIENCE_BOTTLE()); diff --git a/src/entity/Human.php b/src/entity/Human.php index ae1aa2464..30142b91d 100644 --- a/src/entity/Human.php +++ b/src/entity/Human.php @@ -76,7 +76,7 @@ use function array_key_exists; use function array_merge; use function array_values; use function min; -use function random_int; +use function mt_rand; class Human extends Living implements ProjectileSource, InventoryHolder{ @@ -211,6 +211,18 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ return $this->xpManager; } + public function getEnchantmentSeed() : int{ + return $this->xpSeed; + } + + public function setEnchantmentSeed(int $seed) : void{ + $this->xpSeed = $seed; + } + + public function generateEnchantmentSeed() : int{ + return mt_rand(Limits::INT32_MIN, Limits::INT32_MAX); + } + public function getXpDropAmount() : int{ //this causes some XP to be lost on death when above level 1 (by design), dropping at most enough points for //about 7.5 levels of XP. @@ -334,7 +346,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ if(($xpSeedTag = $nbt->getTag(self::TAG_XP_SEED)) instanceof IntTag){ $this->xpSeed = $xpSeedTag->getValue(); }else{ - $this->xpSeed = random_int(Limits::INT32_MIN, Limits::INT32_MAX); + $this->xpSeed = $this->generateEnchantmentSeed(); } } diff --git a/src/event/player/PlayerEnchantOptionsRequestEvent.php b/src/event/player/PlayerEnchantOptionsRequestEvent.php new file mode 100644 index 000000000..d0448bfd8 --- /dev/null +++ b/src/event/player/PlayerEnchantOptionsRequestEvent.php @@ -0,0 +1,75 @@ +player = $player; + } + + public function getEnchantInventory() : EnchantInventory{ + return $this->enchantInventory; + } + + /** + * @return EnchantOption[] + */ + public function getOptions() : array{ + return $this->options; + } + + /** + * @param EnchantOption[] $options + */ + public function setOptions(array $options) : void{ + Utils::validateArrayValueType($options, function(EnchantOption $_) : void{ }); + if(($optionCount = count($options)) > 3){ + throw new \LogicException("The maximum number of options for an enchanting table is 3, but $optionCount have been passed"); + } + + $this->options = $options; + } +} diff --git a/src/event/player/PlayerItemEnchantEvent.php b/src/event/player/PlayerItemEnchantEvent.php new file mode 100644 index 000000000..9974ab40a --- /dev/null +++ b/src/event/player/PlayerItemEnchantEvent.php @@ -0,0 +1,85 @@ +player = $player; + } + + /** + * Returns the inventory transaction involved in this enchant event. + */ + public function getTransaction() : EnchantTransaction{ + return $this->transaction; + } + + /** + * Returns the enchantment option used. + */ + public function getOption() : EnchantOption{ + return $this->option; + } + + /** + * Returns the item to be enchanted. + */ + public function getInputItem() : Item{ + return clone $this->inputItem; + } + + /** + * Returns the enchanted item. + */ + public function getOutputItem() : Item{ + return clone $this->outputItem; + } + + /** + * Returns the number of XP levels and lapis that will be subtracted after enchanting + * if the player is not in creative mode. + */ + public function getCost() : int{ + return $this->cost; + } +} diff --git a/src/inventory/transaction/EnchantTransaction.php b/src/inventory/transaction/EnchantTransaction.php new file mode 100644 index 000000000..f3760e479 --- /dev/null +++ b/src/inventory/transaction/EnchantTransaction.php @@ -0,0 +1,132 @@ +inputItem === null || $this->outputItem === null){ + throw new AssumptionFailedError("Expected that inputItem and outputItem are not null before validating output"); + } + + $enchantedInput = EnchantmentHelper::enchantItem($this->inputItem, $this->option->getEnchantments()); + if(!$this->outputItem->equalsExact($enchantedInput)){ + throw new TransactionValidationException("Invalid output item"); + } + } + + private function validateFiniteResources(int $lapisSpent) : void{ + if($lapisSpent !== $this->cost){ + throw new TransactionValidationException("Expected the amount of lapis lazuli spent to be $this->cost, but received $lapisSpent"); + } + + $xpLevel = $this->source->getXpManager()->getXpLevel(); + $requiredXpLevel = $this->option->getRequiredXpLevel(); + + 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"); + } + } + + public function validate() : void{ + if(count($this->actions) < 1){ + throw new TransactionValidationException("Transaction must have at least one action to be executable"); + } + + /** @var Item[] $inputs */ + $inputs = []; + /** @var Item[] $outputs */ + $outputs = []; + $this->matchItems($outputs, $inputs); + + $lapisSpent = 0; + foreach($inputs as $input){ + if($input->getTypeId() === ItemTypeIds::LAPIS_LAZULI){ + $lapisSpent = $input->getCount(); + }else{ + if($this->inputItem !== null){ + throw new TransactionValidationException("Received more than 1 items to enchant"); + } + $this->inputItem = $input; + } + } + + if($this->inputItem === null){ + throw new TransactionValidationException("No item to enchant received"); + } + + if(($outputCount = count($outputs)) !== 1){ + throw new TransactionValidationException("Expected 1 output item, but received $outputCount"); + } + $this->outputItem = $outputs[0]; + + $this->validateOutput(); + + if($this->source->hasFiniteResources()){ + $this->validateFiniteResources($lapisSpent); + } + } + + public function execute() : void{ + parent::execute(); + + if($this->source->hasFiniteResources()){ + $this->source->getXpManager()->subtractXpLevels($this->cost); + } + $this->source->setEnchantmentSeed($this->source->generateEnchantmentSeed()); + } + + protected function callExecuteEvent() : bool{ + if($this->inputItem === null || $this->outputItem === null){ + throw new AssumptionFailedError("Expected that inputItem and outputItem are not null before executing the event"); + } + + $event = new PlayerItemEnchantEvent($this->source, $this, $this->option, $this->inputItem, $this->outputItem, $this->cost); + $event->call(); + return !$event->isCancelled(); + } +} diff --git a/src/item/Armor.php b/src/item/Armor.php index 6fb538cd6..e52732caf 100644 --- a/src/item/Armor.php +++ b/src/item/Armor.php @@ -44,8 +44,11 @@ class Armor extends Durable{ protected ?Color $customColor = null; - public function __construct(ItemIdentifier $identifier, string $name, ArmorTypeInfo $info){ - parent::__construct($identifier, $name); + /** + * @param string[] $enchantmentTags + */ + public function __construct(ItemIdentifier $identifier, string $name, ArmorTypeInfo $info, array $enchantmentTags = []){ + parent::__construct($identifier, $name, $enchantmentTags); $this->armorInfo = $info; } @@ -72,6 +75,14 @@ class Armor extends Durable{ return $this->armorInfo->isFireProof(); } + public function getMaterial() : ArmorMaterial{ + return $this->armorInfo->getMaterial(); + } + + public function getEnchantability() : int{ + return $this->armorInfo->getMaterial()->getEnchantability(); + } + /** * Returns the dyed colour of this armour piece. This generally only applies to leather armour. */ diff --git a/src/item/ArmorMaterial.php b/src/item/ArmorMaterial.php new file mode 100644 index 000000000..c7915bacc --- /dev/null +++ b/src/item/ArmorMaterial.php @@ -0,0 +1,42 @@ +enchantability; + } +} diff --git a/src/item/ArmorTypeInfo.php b/src/item/ArmorTypeInfo.php index 580b73df3..dbb4ed06d 100644 --- a/src/item/ArmorTypeInfo.php +++ b/src/item/ArmorTypeInfo.php @@ -24,13 +24,18 @@ declare(strict_types=1); namespace pocketmine\item; class ArmorTypeInfo{ + private ArmorMaterial $material; + public function __construct( private int $defensePoints, private int $maxDurability, private int $armorSlot, private int $toughness = 0, - private bool $fireProof = false - ){} + private bool $fireProof = false, + ?ArmorMaterial $material = null + ){ + $this->material = $material ?? VanillaArmorMaterials::LEATHER(); + } public function getDefensePoints() : int{ return $this->defensePoints; @@ -51,4 +56,8 @@ class ArmorTypeInfo{ public function isFireProof() : bool{ return $this->fireProof; } + + public function getMaterial() : ArmorMaterial{ + return $this->material; + } } diff --git a/src/item/EnchantedBook.php b/src/item/EnchantedBook.php new file mode 100644 index 000000000..5660de6f6 --- /dev/null +++ b/src/item/EnchantedBook.php @@ -0,0 +1,30 @@ +nbt = new CompoundTag(); } @@ -455,6 +458,29 @@ class Item implements \JsonSerializable{ return $this->name; } + /** + * Returns tags that represent the type of item being enchanted and are used to determine + * what enchantments can be applied to this item during in-game enchanting (enchanting table, anvil, fishing, etc.). + * @see ItemEnchantmentTags + * @see ItemEnchantmentTagRegistry + * @see AvailableEnchantmentRegistry + * + * @return string[] + */ + public function getEnchantmentTags() : array{ + return $this->enchantmentTags; + } + + /** + * Returns the value that defines how enchantable the item is. + * + * The higher an item's enchantability is, the more likely it will be to gain high-level enchantments + * or multiple enchantments upon being enchanted in an enchanting table. + */ + public function getEnchantability() : int{ + return 1; + } + final public function canBePlaced() : bool{ return $this->getBlock()->canBePlaced(); } diff --git a/src/item/ItemBlock.php b/src/item/ItemBlock.php index fbbe2efeb..11bcb58d3 100644 --- a/src/item/ItemBlock.php +++ b/src/item/ItemBlock.php @@ -36,7 +36,7 @@ final class ItemBlock extends Item{ public function __construct( private Block $block ){ - parent::__construct(ItemIdentifier::fromBlock($block), $block->getName()); + parent::__construct(ItemIdentifier::fromBlock($block), $block->getName(), $block->getEnchantmentTags()); } protected function describeState(RuntimeDataDescriber $w) : void{ diff --git a/src/item/ItemTypeIds.php b/src/item/ItemTypeIds.php index f37426c56..823339766 100644 --- a/src/item/ItemTypeIds.php +++ b/src/item/ItemTypeIds.php @@ -303,8 +303,9 @@ final class ItemTypeIds{ public const MANGROVE_BOAT = 20264; public const GLOW_BERRIES = 20265; public const CHERRY_SIGN = 20266; + public const ENCHANTED_BOOK = 20267; - public const FIRST_UNUSED_ITEM_ID = 20267; + public const FIRST_UNUSED_ITEM_ID = 20268; private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID; diff --git a/src/item/StringToItemParser.php b/src/item/StringToItemParser.php index c8be97cad..d482e4bef 100644 --- a/src/item/StringToItemParser.php +++ b/src/item/StringToItemParser.php @@ -1277,6 +1277,7 @@ final class StringToItemParser extends StringToTParser{ $result->register("egg", fn() => Items::EGG()); $result->register("elixir", fn() => Items::MEDICINE()->setType(MedicineType::ELIXIR())); $result->register("emerald", fn() => Items::EMERALD()); + $result->register("enchanted_book", fn() => Items::ENCHANTED_BOOK()); $result->register("enchanted_golden_apple", fn() => Items::ENCHANTED_GOLDEN_APPLE()); $result->register("enchanting_bottle", fn() => Items::EXPERIENCE_BOTTLE()); $result->register("ender_pearl", fn() => Items::ENDER_PEARL()); diff --git a/src/item/TieredTool.php b/src/item/TieredTool.php index e7d4f2201..dc00aebcf 100644 --- a/src/item/TieredTool.php +++ b/src/item/TieredTool.php @@ -26,8 +26,11 @@ namespace pocketmine\item; abstract class TieredTool extends Tool{ protected ToolTier $tier; - public function __construct(ItemIdentifier $identifier, string $name, ToolTier $tier){ - parent::__construct($identifier, $name); + /** + * @param string[] $enchantmentTags + */ + public function __construct(ItemIdentifier $identifier, string $name, ToolTier $tier, array $enchantmentTags = []){ + parent::__construct($identifier, $name, $enchantmentTags); $this->tier = $tier; } @@ -43,6 +46,10 @@ abstract class TieredTool extends Tool{ return $this->tier->getBaseEfficiency(); } + public function getEnchantability() : int{ + return $this->tier->getEnchantability(); + } + public function getFuelTime() : int{ if($this->tier->equals(ToolTier::WOOD())){ return 200; diff --git a/src/item/ToolTier.php b/src/item/ToolTier.php index 231e233c3..4ca910c0b 100644 --- a/src/item/ToolTier.php +++ b/src/item/ToolTier.php @@ -45,12 +45,12 @@ final class ToolTier{ protected static function setup() : void{ self::registerAll( - new self("wood", 1, 60, 5, 2), - new self("gold", 2, 33, 5, 12), - new self("stone", 3, 132, 6, 4), - new self("iron", 4, 251, 7, 6), - new self("diamond", 5, 1562, 8, 8), - new self("netherite", 6, 2032, 9, 9) + new self("wood", 1, 60, 5, 2, 15), + new self("gold", 2, 33, 5, 12, 22), + new self("stone", 3, 132, 6, 4, 5), + new self("iron", 4, 251, 7, 6, 14), + new self("diamond", 5, 1562, 8, 8, 10), + new self("netherite", 6, 2032, 9, 9, 15) ); } @@ -59,7 +59,8 @@ final class ToolTier{ private int $harvestLevel, private int $maxDurability, private int $baseAttackPoints, - private int $baseEfficiency + private int $baseEfficiency, + private int $enchantability ){ $this->Enum___construct($name); } @@ -79,4 +80,14 @@ final class ToolTier{ public function getBaseEfficiency() : int{ return $this->baseEfficiency; } + + /** + * Returns the value that defines how enchantable the item is. + * + * The higher an item's enchantability is, the more likely it will be to gain high-level enchantments + * or multiple enchantments upon being enchanted in an enchanting table. + */ + public function getEnchantability() : int{ + return $this->enchantability; + } } diff --git a/src/item/VanillaArmorMaterials.php b/src/item/VanillaArmorMaterials.php new file mode 100644 index 000000000..ab2909bce --- /dev/null +++ b/src/item/VanillaArmorMaterials.php @@ -0,0 +1,73 @@ + + */ + public static function getAll() : array{ + // phpstan doesn't support generic traits yet :( + /** @var ArmorMaterial[] $result */ + $result = self::_registryGetAll(); + return $result; + } + + protected static function setup() : void{ + self::register("leather", new ArmorMaterial(15)); + self::register("chainmail", new ArmorMaterial(12)); + self::register("iron", new ArmorMaterial(9)); + self::register("turtle", new ArmorMaterial(9)); + self::register("gold", new ArmorMaterial(25)); + self::register("diamond", new ArmorMaterial(10)); + self::register("netherite", new ArmorMaterial(15)); + } +} diff --git a/src/item/VanillaItems.php b/src/item/VanillaItems.php index b7c32ebc1..07ba398d5 100644 --- a/src/item/VanillaItems.php +++ b/src/item/VanillaItems.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace pocketmine\item; use pocketmine\block\utils\RecordType; -use pocketmine\block\VanillaBlocks; use pocketmine\block\VanillaBlocks as Blocks; use pocketmine\entity\Entity; use pocketmine\entity\Location; @@ -32,8 +31,10 @@ use pocketmine\entity\Squid; use pocketmine\entity\Villager; use pocketmine\entity\Zombie; use pocketmine\inventory\ArmorInventory; +use pocketmine\item\enchantment\ItemEnchantmentTags as EnchantmentTags; use pocketmine\item\ItemIdentifier as IID; use pocketmine\item\ItemTypeIds as Ids; +use pocketmine\item\VanillaArmorMaterials as ArmorMaterials; use pocketmine\math\Vector3; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\CloningRegistryTrait; @@ -151,6 +152,7 @@ use pocketmine\world\World; * @method static Item ECHO_SHARD() * @method static Egg EGG() * @method static Item EMERALD() + * @method static EnchantedBook ENCHANTED_BOOK() * @method static GoldenAppleEnchanted ENCHANTED_GOLDEN_APPLE() * @method static EnderPearl ENDER_PEARL() * @method static ExperienceBottle EXPERIENCE_BOTTLE() @@ -337,7 +339,7 @@ final class VanillaItems{ self::registerSpawnEggs(); self::registerTierToolItems(); - self::register("air", VanillaBlocks::AIR()->asItem()->setCount(0)); + self::register("air", Blocks::AIR()->asItem()->setCount(0)); self::register("acacia_sign", new ItemBlockWallOrFloor(new IID(Ids::ACACIA_SIGN), Blocks::ACACIA_SIGN(), Blocks::ACACIA_WALL_SIGN())); self::register("amethyst_shard", new Item(new IID(Ids::AMETHYST_SHARD), "Amethyst Shard")); @@ -355,8 +357,8 @@ final class VanillaItems{ self::register("bleach", new Item(new IID(Ids::BLEACH), "Bleach")); self::register("bone", new Item(new IID(Ids::BONE), "Bone")); self::register("bone_meal", new Fertilizer(new IID(Ids::BONE_MEAL), "Bone Meal")); - self::register("book", new Book(new IID(Ids::BOOK), "Book")); - self::register("bow", new Bow(new IID(Ids::BOW), "Bow")); + self::register("book", new Book(new IID(Ids::BOOK), "Book", [EnchantmentTags::ALL])); + self::register("bow", new Bow(new IID(Ids::BOW), "Bow", [EnchantmentTags::BOW])); self::register("bowl", new Bowl(new IID(Ids::BOWL), "Bowl")); self::register("bread", new Bread(new IID(Ids::BREAD), "Bread")); self::register("brick", new Item(new IID(Ids::BRICK), "Brick")); @@ -408,7 +410,7 @@ final class VanillaItems{ self::register("clownfish", new Clownfish(new IID(Ids::CLOWNFISH), "Clownfish")); self::register("coal", new Coal(new IID(Ids::COAL), "Coal")); self::register("cocoa_beans", new CocoaBeans(new IID(Ids::COCOA_BEANS), "Cocoa Beans")); - self::register("compass", new Compass(new IID(Ids::COMPASS), "Compass")); + self::register("compass", new Compass(new IID(Ids::COMPASS), "Compass", [EnchantmentTags::COMPASS])); self::register("cooked_chicken", new CookedChicken(new IID(Ids::COOKED_CHICKEN), "Cooked Chicken")); self::register("cooked_fish", new CookedFish(new IID(Ids::COOKED_FISH), "Cooked Fish")); self::register("cooked_mutton", new CookedMutton(new IID(Ids::COOKED_MUTTON), "Cooked Mutton")); @@ -429,15 +431,16 @@ 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_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")); self::register("feather", new Item(new IID(Ids::FEATHER), "Feather")); self::register("fermented_spider_eye", new Item(new IID(Ids::FERMENTED_SPIDER_EYE), "Fermented Spider Eye")); self::register("fire_charge", new FireCharge(new IID(Ids::FIRE_CHARGE), "Fire Charge")); - self::register("fishing_rod", new FishingRod(new IID(Ids::FISHING_ROD), "Fishing Rod")); + self::register("fishing_rod", new FishingRod(new IID(Ids::FISHING_ROD), "Fishing Rod", [EnchantmentTags::FISHING_ROD])); self::register("flint", new Item(new IID(Ids::FLINT), "Flint")); - self::register("flint_and_steel", new FlintSteel(new IID(Ids::FLINT_AND_STEEL), "Flint and Steel")); + self::register("flint_and_steel", new FlintSteel(new IID(Ids::FLINT_AND_STEEL), "Flint and Steel", [EnchantmentTags::FLINT_AND_STEEL])); self::register("ghast_tear", new Item(new IID(Ids::GHAST_TEAR), "Ghast Tear")); self::register("glass_bottle", new GlassBottle(new IID(Ids::GLASS_BOTTLE), "Glass Bottle")); self::register("glistering_melon", new Item(new IID(Ids::GLISTERING_MELON), "Glistering Melon")); @@ -521,7 +524,7 @@ final class VanillaItems{ self::register("redstone_dust", new Redstone(new IID(Ids::REDSTONE_DUST), "Redstone")); self::register("rotten_flesh", new RottenFlesh(new IID(Ids::ROTTEN_FLESH), "Rotten Flesh")); self::register("scute", new Item(new IID(Ids::SCUTE), "Scute")); - self::register("shears", new Shears(new IID(Ids::SHEARS), "Shears")); + self::register("shears", new Shears(new IID(Ids::SHEARS), "Shears", [EnchantmentTags::SHEARS])); self::register("shulker_shell", new Item(new IID(Ids::SHULKER_SHELL), "Shulker Shell")); self::register("slimeball", new Item(new IID(Ids::SLIMEBALL), "Slimeball")); self::register("snowball", new Snowball(new IID(Ids::SNOWBALL), "Snowball")); @@ -577,67 +580,67 @@ final class VanillaItems{ } private static function registerTierToolItems() : void{ - self::register("diamond_axe", new Axe(new IID(Ids::DIAMOND_AXE), "Diamond Axe", ToolTier::DIAMOND())); - self::register("golden_axe", new Axe(new IID(Ids::GOLDEN_AXE), "Golden Axe", ToolTier::GOLD())); - self::register("iron_axe", new Axe(new IID(Ids::IRON_AXE), "Iron Axe", ToolTier::IRON())); - self::register("netherite_axe", new Axe(new IID(Ids::NETHERITE_AXE), "Netherite Axe", ToolTier::NETHERITE())); - self::register("stone_axe", new Axe(new IID(Ids::STONE_AXE), "Stone Axe", ToolTier::STONE())); - self::register("wooden_axe", new Axe(new IID(Ids::WOODEN_AXE), "Wooden Axe", ToolTier::WOOD())); - self::register("diamond_hoe", new Hoe(new IID(Ids::DIAMOND_HOE), "Diamond Hoe", ToolTier::DIAMOND())); - self::register("golden_hoe", new Hoe(new IID(Ids::GOLDEN_HOE), "Golden Hoe", ToolTier::GOLD())); - self::register("iron_hoe", new Hoe(new IID(Ids::IRON_HOE), "Iron Hoe", ToolTier::IRON())); - self::register("netherite_hoe", new Hoe(new IID(Ids::NETHERITE_HOE), "Netherite Hoe", ToolTier::NETHERITE())); - self::register("stone_hoe", new Hoe(new IID(Ids::STONE_HOE), "Stone Hoe", ToolTier::STONE())); - self::register("wooden_hoe", new Hoe(new IID(Ids::WOODEN_HOE), "Wooden Hoe", ToolTier::WOOD())); - self::register("diamond_pickaxe", new Pickaxe(new IID(Ids::DIAMOND_PICKAXE), "Diamond Pickaxe", ToolTier::DIAMOND())); - self::register("golden_pickaxe", new Pickaxe(new IID(Ids::GOLDEN_PICKAXE), "Golden Pickaxe", ToolTier::GOLD())); - self::register("iron_pickaxe", new Pickaxe(new IID(Ids::IRON_PICKAXE), "Iron Pickaxe", ToolTier::IRON())); - self::register("netherite_pickaxe", new Pickaxe(new IID(Ids::NETHERITE_PICKAXE), "Netherite Pickaxe", ToolTier::NETHERITE())); - self::register("stone_pickaxe", new Pickaxe(new IID(Ids::STONE_PICKAXE), "Stone Pickaxe", ToolTier::STONE())); - self::register("wooden_pickaxe", new Pickaxe(new IID(Ids::WOODEN_PICKAXE), "Wooden Pickaxe", ToolTier::WOOD())); - self::register("diamond_shovel", new Shovel(new IID(Ids::DIAMOND_SHOVEL), "Diamond Shovel", ToolTier::DIAMOND())); - self::register("golden_shovel", new Shovel(new IID(Ids::GOLDEN_SHOVEL), "Golden Shovel", ToolTier::GOLD())); - self::register("iron_shovel", new Shovel(new IID(Ids::IRON_SHOVEL), "Iron Shovel", ToolTier::IRON())); - self::register("netherite_shovel", new Shovel(new IID(Ids::NETHERITE_SHOVEL), "Netherite Shovel", ToolTier::NETHERITE())); - self::register("stone_shovel", new Shovel(new IID(Ids::STONE_SHOVEL), "Stone Shovel", ToolTier::STONE())); - self::register("wooden_shovel", new Shovel(new IID(Ids::WOODEN_SHOVEL), "Wooden Shovel", ToolTier::WOOD())); - self::register("diamond_sword", new Sword(new IID(Ids::DIAMOND_SWORD), "Diamond Sword", ToolTier::DIAMOND())); - self::register("golden_sword", new Sword(new IID(Ids::GOLDEN_SWORD), "Golden Sword", ToolTier::GOLD())); - self::register("iron_sword", new Sword(new IID(Ids::IRON_SWORD), "Iron Sword", ToolTier::IRON())); - self::register("netherite_sword", new Sword(new IID(Ids::NETHERITE_SWORD), "Netherite Sword", ToolTier::NETHERITE())); - self::register("stone_sword", new Sword(new IID(Ids::STONE_SWORD), "Stone Sword", ToolTier::STONE())); - self::register("wooden_sword", new Sword(new IID(Ids::WOODEN_SWORD), "Wooden Sword", ToolTier::WOOD())); + self::register("diamond_axe", new Axe(new IID(Ids::DIAMOND_AXE), "Diamond Axe", ToolTier::DIAMOND(), [EnchantmentTags::AXE])); + self::register("golden_axe", new Axe(new IID(Ids::GOLDEN_AXE), "Golden Axe", ToolTier::GOLD(), [EnchantmentTags::AXE])); + self::register("iron_axe", new Axe(new IID(Ids::IRON_AXE), "Iron Axe", ToolTier::IRON(), [EnchantmentTags::AXE])); + self::register("netherite_axe", new Axe(new IID(Ids::NETHERITE_AXE), "Netherite Axe", ToolTier::NETHERITE(), [EnchantmentTags::AXE])); + self::register("stone_axe", new Axe(new IID(Ids::STONE_AXE), "Stone Axe", ToolTier::STONE(), [EnchantmentTags::AXE])); + self::register("wooden_axe", new Axe(new IID(Ids::WOODEN_AXE), "Wooden Axe", ToolTier::WOOD(), [EnchantmentTags::AXE])); + self::register("diamond_hoe", new Hoe(new IID(Ids::DIAMOND_HOE), "Diamond Hoe", ToolTier::DIAMOND(), [EnchantmentTags::HOE])); + self::register("golden_hoe", new Hoe(new IID(Ids::GOLDEN_HOE), "Golden Hoe", ToolTier::GOLD(), [EnchantmentTags::HOE])); + self::register("iron_hoe", new Hoe(new IID(Ids::IRON_HOE), "Iron Hoe", ToolTier::IRON(), [EnchantmentTags::HOE])); + self::register("netherite_hoe", new Hoe(new IID(Ids::NETHERITE_HOE), "Netherite Hoe", ToolTier::NETHERITE(), [EnchantmentTags::HOE])); + self::register("stone_hoe", new Hoe(new IID(Ids::STONE_HOE), "Stone Hoe", ToolTier::STONE(), [EnchantmentTags::HOE])); + self::register("wooden_hoe", new Hoe(new IID(Ids::WOODEN_HOE), "Wooden Hoe", ToolTier::WOOD(), [EnchantmentTags::HOE])); + self::register("diamond_pickaxe", new Pickaxe(new IID(Ids::DIAMOND_PICKAXE), "Diamond Pickaxe", ToolTier::DIAMOND(), [EnchantmentTags::PICKAXE])); + self::register("golden_pickaxe", new Pickaxe(new IID(Ids::GOLDEN_PICKAXE), "Golden Pickaxe", ToolTier::GOLD(), [EnchantmentTags::PICKAXE])); + self::register("iron_pickaxe", new Pickaxe(new IID(Ids::IRON_PICKAXE), "Iron Pickaxe", ToolTier::IRON(), [EnchantmentTags::PICKAXE])); + self::register("netherite_pickaxe", new Pickaxe(new IID(Ids::NETHERITE_PICKAXE), "Netherite Pickaxe", ToolTier::NETHERITE(), [EnchantmentTags::PICKAXE])); + self::register("stone_pickaxe", new Pickaxe(new IID(Ids::STONE_PICKAXE), "Stone Pickaxe", ToolTier::STONE(), [EnchantmentTags::PICKAXE])); + self::register("wooden_pickaxe", new Pickaxe(new IID(Ids::WOODEN_PICKAXE), "Wooden Pickaxe", ToolTier::WOOD(), [EnchantmentTags::PICKAXE])); + self::register("diamond_shovel", new Shovel(new IID(Ids::DIAMOND_SHOVEL), "Diamond Shovel", ToolTier::DIAMOND(), [EnchantmentTags::SHOVEL])); + self::register("golden_shovel", new Shovel(new IID(Ids::GOLDEN_SHOVEL), "Golden Shovel", ToolTier::GOLD(), [EnchantmentTags::SHOVEL])); + self::register("iron_shovel", new Shovel(new IID(Ids::IRON_SHOVEL), "Iron Shovel", ToolTier::IRON(), [EnchantmentTags::SHOVEL])); + self::register("netherite_shovel", new Shovel(new IID(Ids::NETHERITE_SHOVEL), "Netherite Shovel", ToolTier::NETHERITE(), [EnchantmentTags::SHOVEL])); + self::register("stone_shovel", new Shovel(new IID(Ids::STONE_SHOVEL), "Stone Shovel", ToolTier::STONE(), [EnchantmentTags::SHOVEL])); + self::register("wooden_shovel", new Shovel(new IID(Ids::WOODEN_SHOVEL), "Wooden Shovel", ToolTier::WOOD(), [EnchantmentTags::SHOVEL])); + self::register("diamond_sword", new Sword(new IID(Ids::DIAMOND_SWORD), "Diamond Sword", ToolTier::DIAMOND(), [EnchantmentTags::SWORD])); + self::register("golden_sword", new Sword(new IID(Ids::GOLDEN_SWORD), "Golden Sword", ToolTier::GOLD(), [EnchantmentTags::SWORD])); + self::register("iron_sword", new Sword(new IID(Ids::IRON_SWORD), "Iron Sword", ToolTier::IRON(), [EnchantmentTags::SWORD])); + self::register("netherite_sword", new Sword(new IID(Ids::NETHERITE_SWORD), "Netherite Sword", ToolTier::NETHERITE(), [EnchantmentTags::SWORD])); + self::register("stone_sword", new Sword(new IID(Ids::STONE_SWORD), "Stone Sword", ToolTier::STONE(), [EnchantmentTags::SWORD])); + self::register("wooden_sword", new Sword(new IID(Ids::WOODEN_SWORD), "Wooden Sword", ToolTier::WOOD(), [EnchantmentTags::SWORD])); } private static function registerArmorItems() : void{ - self::register("chainmail_boots", new Armor(new IID(Ids::CHAINMAIL_BOOTS), "Chainmail Boots", new ArmorTypeInfo(1, 196, ArmorInventory::SLOT_FEET))); - self::register("diamond_boots", new Armor(new IID(Ids::DIAMOND_BOOTS), "Diamond Boots", new ArmorTypeInfo(3, 430, ArmorInventory::SLOT_FEET, 2))); - self::register("golden_boots", new Armor(new IID(Ids::GOLDEN_BOOTS), "Golden Boots", new ArmorTypeInfo(1, 92, ArmorInventory::SLOT_FEET))); - self::register("iron_boots", new Armor(new IID(Ids::IRON_BOOTS), "Iron Boots", new ArmorTypeInfo(2, 196, ArmorInventory::SLOT_FEET))); - self::register("leather_boots", new Armor(new IID(Ids::LEATHER_BOOTS), "Leather Boots", new ArmorTypeInfo(1, 66, ArmorInventory::SLOT_FEET))); - self::register("netherite_boots", new Armor(new IID(Ids::NETHERITE_BOOTS), "Netherite Boots", new ArmorTypeInfo(3, 482, ArmorInventory::SLOT_FEET, 3, true))); + self::register("chainmail_boots", new Armor(new IID(Ids::CHAINMAIL_BOOTS), "Chainmail Boots", new ArmorTypeInfo(1, 196, ArmorInventory::SLOT_FEET, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::BOOTS])); + self::register("diamond_boots", new Armor(new IID(Ids::DIAMOND_BOOTS), "Diamond Boots", new ArmorTypeInfo(3, 430, ArmorInventory::SLOT_FEET, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::BOOTS])); + self::register("golden_boots", new Armor(new IID(Ids::GOLDEN_BOOTS), "Golden Boots", new ArmorTypeInfo(1, 92, ArmorInventory::SLOT_FEET, material: ArmorMaterials::GOLD()), [EnchantmentTags::BOOTS])); + self::register("iron_boots", new Armor(new IID(Ids::IRON_BOOTS), "Iron Boots", new ArmorTypeInfo(2, 196, ArmorInventory::SLOT_FEET, material: ArmorMaterials::IRON()), [EnchantmentTags::BOOTS])); + self::register("leather_boots", new Armor(new IID(Ids::LEATHER_BOOTS), "Leather Boots", new ArmorTypeInfo(1, 66, ArmorInventory::SLOT_FEET, material: ArmorMaterials::LEATHER()), [EnchantmentTags::BOOTS])); + self::register("netherite_boots", new Armor(new IID(Ids::NETHERITE_BOOTS), "Netherite Boots", new ArmorTypeInfo(3, 482, ArmorInventory::SLOT_FEET, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::BOOTS])); - self::register("chainmail_chestplate", new Armor(new IID(Ids::CHAINMAIL_CHESTPLATE), "Chainmail Chestplate", new ArmorTypeInfo(5, 241, ArmorInventory::SLOT_CHEST))); - self::register("diamond_chestplate", new Armor(new IID(Ids::DIAMOND_CHESTPLATE), "Diamond Chestplate", new ArmorTypeInfo(8, 529, ArmorInventory::SLOT_CHEST, 2))); - self::register("golden_chestplate", new Armor(new IID(Ids::GOLDEN_CHESTPLATE), "Golden Chestplate", new ArmorTypeInfo(5, 113, ArmorInventory::SLOT_CHEST))); - self::register("iron_chestplate", new Armor(new IID(Ids::IRON_CHESTPLATE), "Iron Chestplate", new ArmorTypeInfo(6, 241, ArmorInventory::SLOT_CHEST))); - self::register("leather_tunic", new Armor(new IID(Ids::LEATHER_TUNIC), "Leather Tunic", new ArmorTypeInfo(3, 81, ArmorInventory::SLOT_CHEST))); - self::register("netherite_chestplate", new Armor(new IID(Ids::NETHERITE_CHESTPLATE), "Netherite Chestplate", new ArmorTypeInfo(8, 593, ArmorInventory::SLOT_CHEST, 3, true))); + self::register("chainmail_chestplate", new Armor(new IID(Ids::CHAINMAIL_CHESTPLATE), "Chainmail Chestplate", new ArmorTypeInfo(5, 241, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::CHESTPLATE])); + self::register("diamond_chestplate", new Armor(new IID(Ids::DIAMOND_CHESTPLATE), "Diamond Chestplate", new ArmorTypeInfo(8, 529, ArmorInventory::SLOT_CHEST, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::CHESTPLATE])); + self::register("golden_chestplate", new Armor(new IID(Ids::GOLDEN_CHESTPLATE), "Golden Chestplate", new ArmorTypeInfo(5, 113, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::GOLD()), [EnchantmentTags::CHESTPLATE])); + self::register("iron_chestplate", new Armor(new IID(Ids::IRON_CHESTPLATE), "Iron Chestplate", new ArmorTypeInfo(6, 241, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::IRON()), [EnchantmentTags::CHESTPLATE])); + self::register("leather_tunic", new Armor(new IID(Ids::LEATHER_TUNIC), "Leather Tunic", new ArmorTypeInfo(3, 81, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::LEATHER()), [EnchantmentTags::CHESTPLATE])); + self::register("netherite_chestplate", new Armor(new IID(Ids::NETHERITE_CHESTPLATE), "Netherite Chestplate", new ArmorTypeInfo(8, 593, ArmorInventory::SLOT_CHEST, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::CHESTPLATE])); - self::register("chainmail_helmet", new Armor(new IID(Ids::CHAINMAIL_HELMET), "Chainmail Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD))); - self::register("diamond_helmet", new Armor(new IID(Ids::DIAMOND_HELMET), "Diamond Helmet", new ArmorTypeInfo(3, 364, ArmorInventory::SLOT_HEAD, 2))); - self::register("golden_helmet", new Armor(new IID(Ids::GOLDEN_HELMET), "Golden Helmet", new ArmorTypeInfo(2, 78, ArmorInventory::SLOT_HEAD))); - self::register("iron_helmet", new Armor(new IID(Ids::IRON_HELMET), "Iron Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD))); - self::register("leather_cap", new Armor(new IID(Ids::LEATHER_CAP), "Leather Cap", new ArmorTypeInfo(1, 56, ArmorInventory::SLOT_HEAD))); - self::register("netherite_helmet", new Armor(new IID(Ids::NETHERITE_HELMET), "Netherite Helmet", new ArmorTypeInfo(3, 408, ArmorInventory::SLOT_HEAD, 3, true))); - self::register("turtle_helmet", new TurtleHelmet(new IID(Ids::TURTLE_HELMET), "Turtle Shell", new ArmorTypeInfo(2, 276, ArmorInventory::SLOT_HEAD))); + self::register("chainmail_helmet", new Armor(new IID(Ids::CHAINMAIL_HELMET), "Chainmail Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::HELMET])); + self::register("diamond_helmet", new Armor(new IID(Ids::DIAMOND_HELMET), "Diamond Helmet", new ArmorTypeInfo(3, 364, ArmorInventory::SLOT_HEAD, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::HELMET])); + self::register("golden_helmet", new Armor(new IID(Ids::GOLDEN_HELMET), "Golden Helmet", new ArmorTypeInfo(2, 78, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::GOLD()), [EnchantmentTags::HELMET])); + self::register("iron_helmet", new Armor(new IID(Ids::IRON_HELMET), "Iron Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::IRON()), [EnchantmentTags::HELMET])); + self::register("leather_cap", new Armor(new IID(Ids::LEATHER_CAP), "Leather Cap", new ArmorTypeInfo(1, 56, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::LEATHER()), [EnchantmentTags::HELMET])); + self::register("netherite_helmet", new Armor(new IID(Ids::NETHERITE_HELMET), "Netherite Helmet", new ArmorTypeInfo(3, 408, ArmorInventory::SLOT_HEAD, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::HELMET])); + self::register("turtle_helmet", new TurtleHelmet(new IID(Ids::TURTLE_HELMET), "Turtle Shell", new ArmorTypeInfo(2, 276, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::TURTLE()), [EnchantmentTags::HELMET])); - self::register("chainmail_leggings", new Armor(new IID(Ids::CHAINMAIL_LEGGINGS), "Chainmail Leggings", new ArmorTypeInfo(4, 226, ArmorInventory::SLOT_LEGS))); - self::register("diamond_leggings", new Armor(new IID(Ids::DIAMOND_LEGGINGS), "Diamond Leggings", new ArmorTypeInfo(6, 496, ArmorInventory::SLOT_LEGS, 2))); - self::register("golden_leggings", new Armor(new IID(Ids::GOLDEN_LEGGINGS), "Golden Leggings", new ArmorTypeInfo(3, 106, ArmorInventory::SLOT_LEGS))); - self::register("iron_leggings", new Armor(new IID(Ids::IRON_LEGGINGS), "Iron Leggings", new ArmorTypeInfo(5, 226, ArmorInventory::SLOT_LEGS))); - self::register("leather_pants", new Armor(new IID(Ids::LEATHER_PANTS), "Leather Pants", new ArmorTypeInfo(2, 76, ArmorInventory::SLOT_LEGS))); - self::register("netherite_leggings", new Armor(new IID(Ids::NETHERITE_LEGGINGS), "Netherite Leggings", new ArmorTypeInfo(6, 556, ArmorInventory::SLOT_LEGS, 3, true))); + self::register("chainmail_leggings", new Armor(new IID(Ids::CHAINMAIL_LEGGINGS), "Chainmail Leggings", new ArmorTypeInfo(4, 226, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::LEGGINGS])); + self::register("diamond_leggings", new Armor(new IID(Ids::DIAMOND_LEGGINGS), "Diamond Leggings", new ArmorTypeInfo(6, 496, ArmorInventory::SLOT_LEGS, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::LEGGINGS])); + self::register("golden_leggings", new Armor(new IID(Ids::GOLDEN_LEGGINGS), "Golden Leggings", new ArmorTypeInfo(3, 106, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::GOLD()), [EnchantmentTags::LEGGINGS])); + self::register("iron_leggings", new Armor(new IID(Ids::IRON_LEGGINGS), "Iron Leggings", new ArmorTypeInfo(5, 226, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::IRON()), [EnchantmentTags::LEGGINGS])); + self::register("leather_pants", new Armor(new IID(Ids::LEATHER_PANTS), "Leather Pants", new ArmorTypeInfo(2, 76, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::LEATHER()), [EnchantmentTags::LEGGINGS])); + self::register("netherite_leggings", new Armor(new IID(Ids::NETHERITE_LEGGINGS), "Netherite Leggings", new ArmorTypeInfo(6, 556, ArmorInventory::SLOT_LEGS, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::LEGGINGS])); } } diff --git a/src/item/enchantment/AvailableEnchantmentRegistry.php b/src/item/enchantment/AvailableEnchantmentRegistry.php new file mode 100644 index 000000000..af8484049 --- /dev/null +++ b/src/item/enchantment/AvailableEnchantmentRegistry.php @@ -0,0 +1,211 @@ +register(Enchantments::PROTECTION(), [Tags::ARMOR], []); + $this->register(Enchantments::FIRE_PROTECTION(), [Tags::ARMOR], []); + $this->register(Enchantments::FEATHER_FALLING(), [Tags::BOOTS], []); + $this->register(Enchantments::BLAST_PROTECTION(), [Tags::ARMOR], []); + $this->register(Enchantments::PROJECTILE_PROTECTION(), [Tags::ARMOR], []); + $this->register(Enchantments::THORNS(), [Tags::CHESTPLATE], [Tags::HELMET, Tags::LEGGINGS, Tags::BOOTS]); + $this->register(Enchantments::RESPIRATION(), [Tags::HELMET], []); + $this->register(Enchantments::SHARPNESS(), [Tags::SWORD, Tags::AXE], []); + $this->register(Enchantments::KNOCKBACK(), [Tags::SWORD], []); + $this->register(Enchantments::FIRE_ASPECT(), [Tags::SWORD], []); + $this->register(Enchantments::EFFICIENCY(), [Tags::DIG_TOOLS], [Tags::SHEARS]); + $this->register(Enchantments::FORTUNE(), [Tags::DIG_TOOLS], []); + $this->register(Enchantments::SILK_TOUCH(), [Tags::DIG_TOOLS], [Tags::SHEARS]); + $this->register( + Enchantments::UNBREAKING(), + [Tags::ARMOR, Tags::WEAPONS, Tags::FISHING_ROD], + [Tags::SHEARS, Tags::FLINT_AND_STEEL, Tags::SHIELD, Tags::CARROT_ON_STICK, Tags::ELYTRA, Tags::BRUSH] + ); + $this->register(Enchantments::POWER(), [Tags::BOW], []); + $this->register(Enchantments::PUNCH(), [Tags::BOW], []); + $this->register(Enchantments::FLAME(), [Tags::BOW], []); + $this->register(Enchantments::INFINITY(), [Tags::BOW], []); + $this->register( + Enchantments::MENDING(), + [], + [Tags::ARMOR, Tags::WEAPONS, Tags::FISHING_ROD, + Tags::SHEARS, Tags::FLINT_AND_STEEL, Tags::SHIELD, Tags::CARROT_ON_STICK, Tags::ELYTRA, Tags::BRUSH] + ); + $this->register(Enchantments::VANISHING(), [], [Tags::ALL]); + $this->register(Enchantments::SWIFT_SNEAK(), [], [Tags::LEGGINGS]); + } + + /** + * @param string[] $primaryItemTags + * @param string[] $secondaryItemTags + */ + public function register(Enchantment $enchantment, array $primaryItemTags, array $secondaryItemTags) : void{ + $this->enchantments[spl_object_id($enchantment)] = $enchantment; + $this->setPrimaryItemTags($enchantment, $primaryItemTags); + $this->setSecondaryItemTags($enchantment, $secondaryItemTags); + } + + public function unregister(Enchantment $enchantment) : void{ + unset($this->enchantments[spl_object_id($enchantment)]); + unset($this->primaryItemTags[spl_object_id($enchantment)]); + unset($this->secondaryItemTags[spl_object_id($enchantment)]); + } + + public function unregisterAll() : void{ + $this->enchantments = []; + $this->primaryItemTags = []; + $this->secondaryItemTags = []; + } + + public function isRegistered(Enchantment $enchantment) : bool{ + return isset($this->enchantments[spl_object_id($enchantment)]); + } + + /** + * Returns primary compatibility tags for the specified enchantment. + * + * An item matching at least one of these tags (or its descendents) can be: + * - Offered this enchantment in an enchanting table + * - Enchanted by any means allowed by secondary tags + * + * @return string[] + */ + public function getPrimaryItemTags(Enchantment $enchantment) : array{ + return $this->primaryItemTags[spl_object_id($enchantment)] ?? []; + } + + /** + * @param string[] $tags + */ + public function setPrimaryItemTags(Enchantment $enchantment, array $tags) : void{ + if(!$this->isRegistered($enchantment)){ + throw new \LogicException("Cannot set primary item tags for non-registered enchantment"); + } + $this->primaryItemTags[spl_object_id($enchantment)] = array_values($tags); + } + + /** + * Returns secondary compatibility tags for the specified enchantment. + * + * An item matching at least one of these tags (or its descendents) can be: + * - Combined with an enchanted book with this enchantment in an anvil + * - Obtained as loot with this enchantment, e.g. fishing, treasure chests, mob equipment, etc. + * + * @return string[] + */ + public function getSecondaryItemTags(Enchantment $enchantment) : array{ + return $this->secondaryItemTags[spl_object_id($enchantment)] ?? []; + } + + /** + * @param string[] $tags + */ + public function setSecondaryItemTags(Enchantment $enchantment, array $tags) : void{ + if(!$this->isRegistered($enchantment)){ + throw new \LogicException("Cannot set secondary item tags for non-registered enchantment"); + } + $this->secondaryItemTags[spl_object_id($enchantment)] = array_values($tags); + } + + /** + * Returns enchantments that can be applied to the specified item in an enchanting table (primary only). + * + * @return Enchantment[] + */ + public function getPrimaryEnchantmentsForItem(Item $item) : array{ + $itemTags = $item->getEnchantmentTags(); + if(count($itemTags) === 0 || $item->hasEnchantments()){ + return []; + } + + return array_filter( + $this->enchantments, + fn(Enchantment $e) => TagRegistry::getInstance()->isTagArrayIntersection($this->getPrimaryItemTags($e), $itemTags) + ); + } + + /** + * Returns all available enchantments compatible with the item. + * + * Warning: not suitable for obtaining enchantments for an enchanting table + * (use {@link AvailableEnchantmentRegistry::getPrimaryEnchantmentsForItem()} for that). + * + * @return Enchantment[] + */ + public function getAllEnchantmentsForItem(Item $item) : array{ + if(count($item->getEnchantmentTags()) === 0){ + return []; + } + + return array_filter( + $this->enchantments, + fn(Enchantment $enchantment) => $this->isAvailableForItem($enchantment, $item) + ); + } + + /** + * Returns whether the specified enchantment can be applied to the particular item. + * + * Warning: not suitable for checking the availability of enchantment for an enchanting table. + */ + public function isAvailableForItem(Enchantment $enchantment, Item $item) : bool{ + $itemTags = $item->getEnchantmentTags(); + $tagRegistry = TagRegistry::getInstance(); + + return $tagRegistry->isTagArrayIntersection($this->getPrimaryItemTags($enchantment), $itemTags) || + $tagRegistry->isTagArrayIntersection($this->getSecondaryItemTags($enchantment), $itemTags); + } + + /** + * @return Enchantment[] + */ + public function getAll() : array{ + return $this->enchantments; + } +} diff --git a/src/item/enchantment/EnchantOption.php b/src/item/enchantment/EnchantOption.php new file mode 100644 index 000000000..b1c3a7af5 --- /dev/null +++ b/src/item/enchantment/EnchantOption.php @@ -0,0 +1,66 @@ +requiredXpLevel; + } + + /** + * Returns the name that will be translated to the 'Standard Galactic Alphabet' client-side. + * This can be any arbitrary text string, since the vanilla client cannot read the text anyway. + * Example: 'bless creature range free'. + */ + public function getDisplayName() : string{ + return $this->displayName; + } + + /** + * Returns the enchantments that will be applied to the item when this option is clicked. + * + * @return EnchantmentInstance[] + */ + public function getEnchantments() : array{ + return $this->enchantments; + } +} diff --git a/src/item/enchantment/Enchantment.php b/src/item/enchantment/Enchantment.php index c53dfab7c..22c0cdb01 100644 --- a/src/item/enchantment/Enchantment.php +++ b/src/item/enchantment/Enchantment.php @@ -23,9 +23,13 @@ declare(strict_types=1); namespace pocketmine\item\enchantment; +use DaveRandom\CallbackValidator\CallbackType; +use DaveRandom\CallbackValidator\ParameterType; +use DaveRandom\CallbackValidator\ReturnType; use pocketmine\lang\Translatable; use pocketmine\utils\NotCloneable; use pocketmine\utils\NotSerializable; +use pocketmine\utils\Utils; /** * Manages enchantment type data. @@ -34,13 +38,32 @@ class Enchantment{ use NotCloneable; use NotSerializable; + /** @var \Closure(int $level) : int $minEnchantingPower */ + private \Closure $minEnchantingPower; + + /** + * @phpstan-param null|(\Closure(int $level) : int) $minEnchantingPower + * + * @param int $primaryItemFlags @deprecated + * @param int $secondaryItemFlags @deprecated + * @param int $enchantingPowerRange Value used to calculate the maximum enchanting power (minEnchantingPower + enchantingPowerRange) + */ public function __construct( private Translatable|string $name, private int $rarity, private int $primaryItemFlags, private int $secondaryItemFlags, - private int $maxLevel - ){} + private int $maxLevel, + ?\Closure $minEnchantingPower = null, + private int $enchantingPowerRange = 50 + ){ + $this->minEnchantingPower = $minEnchantingPower ?? fn(int $level) : int => 1; + + Utils::validateCallableSignature(new CallbackType( + new ReturnType("int"), + new ParameterType("level", "int") + ), $this->minEnchantingPower); + } /** * Returns a translation key for this enchantment's name. @@ -58,6 +81,8 @@ class Enchantment{ /** * Returns a bitset indicating what item types can have this item applied from an enchanting table. + * + * @deprecated */ public function getPrimaryItemFlags() : int{ return $this->primaryItemFlags; @@ -66,6 +91,8 @@ class Enchantment{ /** * Returns a bitset indicating what item types cannot have this item applied from an enchanting table, but can from * an anvil. + * + * @deprecated */ public function getSecondaryItemFlags() : int{ return $this->secondaryItemFlags; @@ -73,6 +100,8 @@ class Enchantment{ /** * Returns whether this enchantment can apply to the item type from an enchanting table. + * + * @deprecated */ public function hasPrimaryItemType(int $flag) : bool{ return ($this->primaryItemFlags & $flag) !== 0; @@ -80,6 +109,8 @@ class Enchantment{ /** * Returns whether this enchantment can apply to the item type from an anvil, if it is not a primary item. + * + * @deprecated */ public function hasSecondaryItemType(int $flag) : bool{ return ($this->secondaryItemFlags & $flag) !== 0; @@ -92,5 +123,34 @@ class Enchantment{ return $this->maxLevel; } - //TODO: methods for min/max XP cost bounds based on enchantment level (not needed yet - enchanting is client-side) + /** + * Returns whether this enchantment can be applied to the item along with the given enchantment. + */ + public function isCompatibleWith(Enchantment $other) : bool{ + return IncompatibleEnchantmentRegistry::getInstance()->areCompatible($this, $other); + } + + /** + * Returns the minimum enchanting power value required for the particular level of the enchantment + * to be available in an enchanting table. + * + * Enchanting power is a random value based on the number of bookshelves around an enchanting table + * and the enchantability of the item being enchanted. It is only used when determining the available + * enchantments for the enchantment options. + */ + public function getMinEnchantingPower(int $level) : int{ + return ($this->minEnchantingPower)($level); + } + + /** + * Returns the maximum enchanting power value allowed for the particular level of the enchantment + * to be available in an enchanting table. + * + * Enchanting power is a random value based on the number of bookshelves around an enchanting table + * and the enchantability of the item being enchanted. It is only used when determining the available + * enchantments for the enchantment options. + */ + public function getMaxEnchantingPower(int $level) : int{ + return $this->getMinEnchantingPower($level) + $this->enchantingPowerRange; + } } diff --git a/src/item/enchantment/EnchantmentHelper.php b/src/item/enchantment/EnchantmentHelper.php new file mode 100644 index 000000000..bd4b68963 --- /dev/null +++ b/src/item/enchantment/EnchantmentHelper.php @@ -0,0 +1,217 @@ +getTypeId() === ItemTypeIds::BOOK ? Items::ENCHANTED_BOOK() : clone $item; + + foreach($enchantments as $enchantment){ + $resultItem->addEnchantment($enchantment); + } + + return $resultItem; + } + + /** + * @return EnchantOption[] + */ + public static function getEnchantOptions(Position $tablePos, Item $input, int $seed) : array{ + if($input->isNull() || $input->hasEnchantments()){ + return []; + } + + $random = new Random($seed); + + $bookshelfCount = self::countBookshelves($tablePos); + $baseRequiredLevel = $random->nextRange(1, 8) + ($bookshelfCount >> 1) + $random->nextRange(0, $bookshelfCount); + $topRequiredLevel = (int) floor(max($baseRequiredLevel / 3, 1)); + $middleRequiredLevel = (int) floor($baseRequiredLevel * 2 / 3 + 1); + $bottomRequiredLevel = max($baseRequiredLevel, $bookshelfCount * 2); + + return [ + self::createEnchantOption($random, $input, $topRequiredLevel), + self::createEnchantOption($random, $input, $middleRequiredLevel), + self::createEnchantOption($random, $input, $bottomRequiredLevel), + ]; + } + + private static function countBookshelves(Position $tablePos) : int{ + $bookshelfCount = 0; + $world = $tablePos->getWorld(); + + for($x = -2; $x <= 2; $x++){ + for($z = -2; $z <= 2; $z++){ + // We only check blocks at a distance of 2 blocks from the enchanting table + if(abs($x) !== 2 && abs($z) !== 2){ + continue; + } + + // Ensure the space between the bookshelf stack at this X/Z and the enchanting table is empty + for($y = 0; $y <= 1; $y++){ + // Calculate the coordinates of the space between the bookshelf and the enchanting table + $spaceX = max(min($x, 1), -1); + $spaceZ = max(min($z, 1), -1); + $spaceBlock = $world->getBlock($tablePos->add($spaceX, $y, $spaceZ)); + if($spaceBlock->getTypeId() !== BlockTypeIds::AIR){ + continue 2; + } + } + + // Finally, check the number of bookshelves at the current position + for($y = 0; $y <= 1; $y++){ + $block = $world->getBlock($tablePos->add($x, $y, $z)); + if($block->getTypeId() === BlockTypeIds::BOOKSHELF){ + $bookshelfCount++; + if($bookshelfCount === self::MAX_BOOKSHELF_COUNT){ + return $bookshelfCount; + } + } + } + } + } + + return $bookshelfCount; + } + + private static function createEnchantOption(Random $random, Item $inputItem, int $requiredXpLevel) : EnchantOption{ + $enchantingPower = $requiredXpLevel; + + $enchantability = $inputItem->getEnchantability(); + $enchantingPower = $enchantingPower + $random->nextRange(0, $enchantability >> 2) + $random->nextRange(0, $enchantability >> 2) + 1; + // Random bonus for enchanting power between 0.85 and 1.15 + $bonus = 1 + ($random->nextFloat() + $random->nextFloat() - 1) * 0.15; + $enchantingPower = (int) round($enchantingPower * $bonus); + + $resultEnchantments = []; + $availableEnchantments = self::getAvailableEnchantments($enchantingPower, $inputItem); + + $lastEnchantment = self::getRandomWeightedEnchantment($random, $availableEnchantments); + if($lastEnchantment !== null){ + $resultEnchantments[] = $lastEnchantment; + + // With probability (power + 1) / 50, continue adding enchantments + while($random->nextFloat() <= ($enchantingPower + 1) / 50){ + // Remove from the list of available enchantments anything that conflicts + // with previously-chosen enchantments + $availableEnchantments = array_filter( + $availableEnchantments, + function(EnchantmentInstance $e) use ($lastEnchantment){ + return $e->getType() !== $lastEnchantment->getType() && + $e->getType()->isCompatibleWith($lastEnchantment->getType()); + } + ); + + $lastEnchantment = self::getRandomWeightedEnchantment($random, $availableEnchantments); + if($lastEnchantment === null){ + break; + } + + $resultEnchantments[] = $lastEnchantment; + $enchantingPower >>= 1; + } + } + + return new EnchantOption($requiredXpLevel, self::getRandomOptionName($random), $resultEnchantments); + } + + /** + * @return EnchantmentInstance[] + */ + private static function getAvailableEnchantments(int $enchantingPower, Item $item) : array{ + $list = []; + + foreach(EnchantmentRegistry::getInstance()->getPrimaryEnchantmentsForItem($item) as $enchantment){ + for($lvl = $enchantment->getMaxLevel(); $lvl > 0; $lvl--){ + if($enchantingPower >= $enchantment->getMinEnchantingPower($lvl) && + $enchantingPower <= $enchantment->getMaxEnchantingPower($lvl) + ){ + $list[] = new EnchantmentInstance($enchantment, $lvl); + break; + } + } + } + + return $list; + } + + /** + * @param EnchantmentInstance[] $enchantments + */ + private static function getRandomWeightedEnchantment(Random $random, array $enchantments) : ?EnchantmentInstance{ + if(count($enchantments) === 0){ + return null; + } + + $totalWeight = 0; + foreach($enchantments as $enchantment){ + $totalWeight += $enchantment->getType()->getRarity(); + } + + $result = null; + $randomWeight = $random->nextRange(1, $totalWeight); + + foreach($enchantments as $enchantment){ + $randomWeight -= $enchantment->getType()->getRarity(); + + if($randomWeight <= 0){ + $result = $enchantment; + break; + } + } + + return $result; + } + + private static function getRandomOptionName(Random $random) : string{ + $name = ""; + for($i = $random->nextRange(5, 15); $i > 0; $i--){ + $name .= chr($random->nextRange(ord("a"), ord("z"))); + } + + return $name; + } +} diff --git a/src/item/enchantment/IncompatibleEnchantmentGroups.php b/src/item/enchantment/IncompatibleEnchantmentGroups.php new file mode 100644 index 000000000..ed1141bee --- /dev/null +++ b/src/item/enchantment/IncompatibleEnchantmentGroups.php @@ -0,0 +1,34 @@ +> + * @var true[][] + */ + private array $incompatibilityMap = []; + + private function __construct(){ + $this->register(Groups::PROTECTION, [Enchantments::PROTECTION(), Enchantments::FIRE_PROTECTION(), Enchantments::BLAST_PROTECTION(), Enchantments::PROJECTILE_PROTECTION()]); + $this->register(Groups::BOW_INFINITE, [Enchantments::INFINITY(), Enchantments::MENDING()]); + $this->register(Groups::DIG_DROP, [Enchantments::FORTUNE(), Enchantments::SILK_TOUCH()]); + } + + /** + * Register incompatibility for an enchantment group. + * + * All enchantments belonging to the same group are incompatible with each other, + * i.e. they cannot be added together on the same item. + * + * @param Enchantment[] $enchantments + */ + public function register(string $tag, array $enchantments) : void{ + foreach($enchantments as $enchantment){ + $this->incompatibilityMap[spl_object_id($enchantment)][$tag] = true; + } + } + + /** + * Unregister incompatibility for some enchantments of a particular group. + * + * @param Enchantment[] $enchantments + */ + public function unregister(string $tag, array $enchantments) : void{ + foreach($enchantments as $enchantment){ + unset($this->incompatibilityMap[spl_object_id($enchantment)][$tag]); + } + } + + /** + * Unregister incompatibility for all enchantments of a particular group. + */ + public function unregisterAll(string $tag) : void{ + foreach($this->incompatibilityMap as $id => $tags){ + unset($this->incompatibilityMap[$id][$tag]); + } + } + + /** + * Returns whether two enchantments can be applied to the same item. + */ + public function areCompatible(Enchantment $first, Enchantment $second) : bool{ + $firstIncompatibilities = $this->incompatibilityMap[spl_object_id($first)] ?? []; + $secondIncompatibilities = $this->incompatibilityMap[spl_object_id($second)] ?? []; + return count(array_intersect_key($firstIncompatibilities, $secondIncompatibilities)) === 0; + } +} diff --git a/src/item/enchantment/ItemEnchantmentTagRegistry.php b/src/item/enchantment/ItemEnchantmentTagRegistry.php new file mode 100644 index 000000000..9c607f9d2 --- /dev/null +++ b/src/item/enchantment/ItemEnchantmentTagRegistry.php @@ -0,0 +1,190 @@ +> + * @var string[][] + */ + private array $tagMap = []; + + private function __construct(){ + $this->register(Tags::ARMOR, [Tags::HELMET, Tags::CHESTPLATE, Tags::LEGGINGS, Tags::BOOTS]); + $this->register(Tags::SHIELD); + $this->register(Tags::SWORD); + $this->register(Tags::TRIDENT); + $this->register(Tags::BOW); + $this->register(Tags::CROSSBOW); + $this->register(Tags::SHEARS); + $this->register(Tags::FLINT_AND_STEEL); + $this->register(Tags::DIG_TOOLS, [Tags::AXE, Tags::PICKAXE, Tags::SHOVEL, Tags::HOE]); + $this->register(Tags::FISHING_ROD); + $this->register(Tags::CARROT_ON_STICK); + $this->register(Tags::COMPASS); + $this->register(Tags::MASK); + $this->register(Tags::ELYTRA); + $this->register(Tags::BRUSH); + $this->register(Tags::WEAPONS, [ + Tags::SWORD, + Tags::TRIDENT, + Tags::BOW, + Tags::CROSSBOW, + Tags::DIG_TOOLS, + ]); + } + + /** + * Register tag and its nested tags. + * + * @param string[] $nestedTags + */ + public function register(string $tag, array $nestedTags = []) : void{ + $this->assertNotInternalTag($tag); + + foreach($nestedTags as $nestedTag){ + if(!isset($this->tagMap[$nestedTag])){ + $this->register($nestedTag); + } + $this->tagMap[$tag][] = $nestedTag; + } + + if(!isset($this->tagMap[$tag])){ + $this->tagMap[$tag] = []; + $this->tagMap[Tags::ALL][] = $tag; + } + } + + public function unregister(string $tag) : void{ + if(!isset($this->tagMap[$tag])){ + return; + } + $this->assertNotInternalTag($tag); + + unset($this->tagMap[$tag]); + + foreach(Utils::stringifyKeys($this->tagMap) as $key => $nestedTags){ + if(($nestedKey = array_search($tag, $nestedTags, true)) !== false){ + unset($this->tagMap[$key][$nestedKey]); + } + } + } + + /** + * Remove specified nested tags. + * + * @param string[] $nestedTags + */ + public function removeNested(string $tag, array $nestedTags) : void{ + $this->assertNotInternalTag($tag); + $this->tagMap[$tag] = array_diff($this->tagMap[$tag], $nestedTags); + } + + /** + * Returns nested tags of a particular tag. + * + * @return string[] + */ + public function getNested(string $tag) : array{ + return $this->tagMap[$tag] ?? []; + } + + /** + * @param string[] $firstTags + * @param string[] $secondTags + */ + public function isTagArrayIntersection(array $firstTags, array $secondTags) : bool{ + if(count($firstTags) === 0 || count($secondTags) === 0){ + return false; + } + + $firstLeafTags = $this->getLeafTagsForArray($firstTags); + $secondLeafTags = $this->getLeafTagsForArray($secondTags); + + return count(array_intersect($firstLeafTags, $secondLeafTags)) !== 0; + } + + /** + * Returns all tags that are recursively nested within each tag in the array and do not have any nested tags. + * + * @param string[] $tags + * + * @return string[] + */ + private function getLeafTagsForArray(array $tags) : array{ + $leafTagArrays = []; + foreach($tags as $tag){ + $leafTagArrays[] = $this->getLeafTags($tag); + } + return array_unique(array_merge(...$leafTagArrays)); + } + + /** + * Returns all tags that are recursively nested within the given tag and do not have any nested tags. + * + * @return string[] + */ + private function getLeafTags(string $tag) : array{ + $result = []; + $tagsToHandle = [$tag]; + + while(count($tagsToHandle) !== 0){ + $currentTag = array_shift($tagsToHandle); + $nestedTags = $this->getNested($currentTag); + + if(count($nestedTags) === 0){ + $result[] = $currentTag; + }else{ + $tagsToHandle = array_merge($tagsToHandle, $nestedTags); + } + } + + return $result; + } + + private function assertNotInternalTag(string $tag) : void{ + if($tag === Tags::ALL){ + throw new \InvalidArgumentException( + "Cannot perform any operations on the internal item enchantment tag '$tag'" + ); + } + } +} diff --git a/src/item/enchantment/ItemEnchantmentTags.php b/src/item/enchantment/ItemEnchantmentTags.php new file mode 100644 index 000000000..485cb5d0c --- /dev/null +++ b/src/item/enchantment/ItemEnchantmentTags.php @@ -0,0 +1,57 @@ +typeModifier = $typeModifier; if($applicableDamageTypes !== null){ diff --git a/src/item/enchantment/VanillaEnchantments.php b/src/item/enchantment/VanillaEnchantments.php index ac2f5c57e..779098c77 100644 --- a/src/item/enchantment/VanillaEnchantments.php +++ b/src/item/enchantment/VanillaEnchantments.php @@ -59,47 +59,224 @@ final class VanillaEnchantments{ use RegistryTrait; protected static function setup() : void{ - self::register("PROTECTION", new ProtectionEnchantment(KnownTranslationFactory::enchantment_protect_all(), Rarity::COMMON, ItemFlags::ARMOR, ItemFlags::NONE, 4, 0.75, null)); - self::register("FIRE_PROTECTION", new ProtectionEnchantment(KnownTranslationFactory::enchantment_protect_fire(), Rarity::UNCOMMON, ItemFlags::ARMOR, ItemFlags::NONE, 4, 1.25, [ - EntityDamageEvent::CAUSE_FIRE, - EntityDamageEvent::CAUSE_FIRE_TICK, - EntityDamageEvent::CAUSE_LAVA - //TODO: check fireballs - ])); - self::register("FEATHER_FALLING", new ProtectionEnchantment(KnownTranslationFactory::enchantment_protect_fall(), Rarity::UNCOMMON, ItemFlags::FEET, ItemFlags::NONE, 4, 2.5, [ - EntityDamageEvent::CAUSE_FALL - ])); - self::register("BLAST_PROTECTION", new ProtectionEnchantment(KnownTranslationFactory::enchantment_protect_explosion(), Rarity::RARE, ItemFlags::ARMOR, ItemFlags::NONE, 4, 1.5, [ - EntityDamageEvent::CAUSE_BLOCK_EXPLOSION, - EntityDamageEvent::CAUSE_ENTITY_EXPLOSION - ])); - self::register("PROJECTILE_PROTECTION", new ProtectionEnchantment(KnownTranslationFactory::enchantment_protect_projectile(), Rarity::UNCOMMON, ItemFlags::ARMOR, ItemFlags::NONE, 4, 1.5, [ - EntityDamageEvent::CAUSE_PROJECTILE - ])); - self::register("THORNS", new Enchantment(KnownTranslationFactory::enchantment_thorns(), Rarity::MYTHIC, ItemFlags::TORSO, ItemFlags::HEAD | ItemFlags::LEGS | ItemFlags::FEET, 3)); - self::register("RESPIRATION", new Enchantment(KnownTranslationFactory::enchantment_oxygen(), Rarity::RARE, ItemFlags::HEAD, ItemFlags::NONE, 3)); + self::register("PROTECTION", new ProtectionEnchantment( + KnownTranslationFactory::enchantment_protect_all(), + Rarity::COMMON, + 0, + 0, + 4, + 0.75, + null, + fn(int $level) : int => 11 * ($level - 1) + 1, + 20 + )); + self::register("FIRE_PROTECTION", new ProtectionEnchantment( + KnownTranslationFactory::enchantment_protect_fire(), + Rarity::UNCOMMON, + 0, + 0, + 4, + 1.25, + [ + EntityDamageEvent::CAUSE_FIRE, + EntityDamageEvent::CAUSE_FIRE_TICK, + EntityDamageEvent::CAUSE_LAVA + //TODO: check fireballs + ], + fn(int $level) : int => 8 * ($level - 1) + 10, + 12 + )); + self::register("FEATHER_FALLING", new ProtectionEnchantment( + KnownTranslationFactory::enchantment_protect_fall(), + Rarity::UNCOMMON, + 0, + 0, + 4, + 2.5, + [ + EntityDamageEvent::CAUSE_FALL + ], + fn(int $level) : int => 6 * ($level - 1) + 5, + 10 + )); + self::register("BLAST_PROTECTION", new ProtectionEnchantment( + KnownTranslationFactory::enchantment_protect_explosion(), + Rarity::RARE, + 0, + 0, + 4, + 1.5, + [ + EntityDamageEvent::CAUSE_BLOCK_EXPLOSION, + EntityDamageEvent::CAUSE_ENTITY_EXPLOSION + ], + fn(int $level) : int => 8 * ($level - 1) + 5, + 12 + )); + self::register("PROJECTILE_PROTECTION", new ProtectionEnchantment( + KnownTranslationFactory::enchantment_protect_projectile(), + Rarity::UNCOMMON, + 0, + 0, + 4, + 1.5, + [ + EntityDamageEvent::CAUSE_PROJECTILE + ], + fn(int $level) : int => 6 * ($level - 1) + 3, + 15 + )); + self::register("THORNS", new Enchantment( + KnownTranslationFactory::enchantment_thorns(), + Rarity::MYTHIC, + 0, + 0, + 3, + fn(int $level) : int => 20 * ($level - 1) + 10, + 50 + )); + self::register("RESPIRATION", new Enchantment( + KnownTranslationFactory::enchantment_oxygen(), + Rarity::RARE, + 0, + 0, + 3, + fn(int $level) : int => 10 * $level, + 30 + )); - self::register("SHARPNESS", new SharpnessEnchantment(KnownTranslationFactory::enchantment_damage_all(), Rarity::COMMON, ItemFlags::SWORD, ItemFlags::AXE, 5)); - //TODO: smite, bane of arthropods (these don't make sense now because their applicable mobs don't exist yet) + self::register("SHARPNESS", new SharpnessEnchantment( + KnownTranslationFactory::enchantment_damage_all(), + Rarity::COMMON, + 0, + 0, + 5, + fn(int $level) : int => 11 * ($level - 1) + 1, + 20 + )); + self::register("KNOCKBACK", new KnockbackEnchantment( + KnownTranslationFactory::enchantment_knockback(), + Rarity::UNCOMMON, + 0, + 0, + 2, + fn(int $level) : int => 20 * ($level - 1) + 5, + 50 + )); + self::register("FIRE_ASPECT", new FireAspectEnchantment( + KnownTranslationFactory::enchantment_fire(), + Rarity::RARE, + 0, + 0, + 2, + fn(int $level) : int => 20 * ($level - 1) + 10, + 50 + )); + //TODO: smite, bane of arthropods, looting (these don't make sense now because their applicable mobs don't exist yet) - self::register("KNOCKBACK", new KnockbackEnchantment(KnownTranslationFactory::enchantment_knockback(), Rarity::UNCOMMON, ItemFlags::SWORD, ItemFlags::NONE, 2)); - self::register("FIRE_ASPECT", new FireAspectEnchantment(KnownTranslationFactory::enchantment_fire(), Rarity::RARE, ItemFlags::SWORD, ItemFlags::NONE, 2)); + self::register("EFFICIENCY", new Enchantment( + KnownTranslationFactory::enchantment_digging(), + Rarity::COMMON, + 0, + 0, + 5, + fn(int $level) : int => 10 * ($level - 1) + 1, + 50 + )); + self::register("FORTUNE", new Enchantment( + KnownTranslationFactory::enchantment_lootBonusDigger(), + Rarity::RARE, + 0, + 0, + 3, + fn(int $level) : int => 9 * ($level - 1) + 15, + 50 + )); + self::register("SILK_TOUCH", new Enchantment( + KnownTranslationFactory::enchantment_untouching(), + Rarity::MYTHIC, + 0, + 0, + 1, + fn(int $level) : int => 15, + 50 + )); + self::register("UNBREAKING", new Enchantment( + KnownTranslationFactory::enchantment_durability(), + Rarity::UNCOMMON, + 0, + 0, + 3, + fn(int $level) : int => 8 * ($level - 1) + 5, + 50 + )); - self::register("EFFICIENCY", new Enchantment(KnownTranslationFactory::enchantment_digging(), Rarity::COMMON, ItemFlags::DIG, ItemFlags::SHEARS, 5)); - self::register("FORTUNE", new Enchantment(KnownTranslationFactory::enchantment_lootBonusDigger(), Rarity::RARE, ItemFlags::DIG, ItemFlags::NONE, 3)); - self::register("SILK_TOUCH", new Enchantment(KnownTranslationFactory::enchantment_untouching(), Rarity::MYTHIC, ItemFlags::DIG, ItemFlags::SHEARS, 1)); - self::register("UNBREAKING", new Enchantment(KnownTranslationFactory::enchantment_durability(), Rarity::UNCOMMON, ItemFlags::DIG | ItemFlags::ARMOR | ItemFlags::FISHING_ROD | ItemFlags::BOW, ItemFlags::TOOL | ItemFlags::CARROT_STICK | ItemFlags::ELYTRA, 3)); + self::register("POWER", new Enchantment( + KnownTranslationFactory::enchantment_arrowDamage(), + Rarity::COMMON, + 0, + 0, + 5, + fn(int $level) : int => 10 * ($level - 1) + 1, + 15 + )); + self::register("PUNCH", new Enchantment( + KnownTranslationFactory::enchantment_arrowKnockback(), + Rarity::RARE, + 0, + 0, + 2, + fn(int $level) : int => 20 * ($level - 1) + 12, + 25 + )); + self::register("FLAME", new Enchantment( + KnownTranslationFactory::enchantment_arrowFire(), + Rarity::RARE, + 0, + 0, + 1, + fn(int $level) : int => 20, + 30 + )); + self::register("INFINITY", new Enchantment( + KnownTranslationFactory::enchantment_arrowInfinite(), + Rarity::MYTHIC, + 0, + 0, + 1, + fn(int $level) : int => 20, + 30 + )); - self::register("POWER", new Enchantment(KnownTranslationFactory::enchantment_arrowDamage(), Rarity::COMMON, ItemFlags::BOW, ItemFlags::NONE, 5)); - self::register("PUNCH", new Enchantment(KnownTranslationFactory::enchantment_arrowKnockback(), Rarity::RARE, ItemFlags::BOW, ItemFlags::NONE, 2)); - self::register("FLAME", new Enchantment(KnownTranslationFactory::enchantment_arrowFire(), Rarity::RARE, ItemFlags::BOW, ItemFlags::NONE, 1)); - self::register("INFINITY", new Enchantment(KnownTranslationFactory::enchantment_arrowInfinite(), Rarity::MYTHIC, ItemFlags::BOW, ItemFlags::NONE, 1)); + self::register("MENDING", new Enchantment( + KnownTranslationFactory::enchantment_mending(), + Rarity::RARE, + 0, + 0, + 1, + fn(int $level) : int => 25, + 50 + )); - self::register("MENDING", new Enchantment(KnownTranslationFactory::enchantment_mending(), Rarity::RARE, ItemFlags::NONE, ItemFlags::ALL, 1)); + self::register("VANISHING", new Enchantment( + KnownTranslationFactory::enchantment_curse_vanishing(), + Rarity::MYTHIC, + 0, + 0, + 1, + fn(int $level) : int => 25, + 25 + )); - self::register("VANISHING", new Enchantment(KnownTranslationFactory::enchantment_curse_vanishing(), Rarity::MYTHIC, ItemFlags::NONE, ItemFlags::ALL, 1)); - - self::register("SWIFT_SNEAK", new Enchantment(KnownTranslationFactory::enchantment_swift_sneak(), Rarity::MYTHIC, ItemFlags::NONE, ItemFlags::LEGS, 3)); + self::register("SWIFT_SNEAK", new Enchantment( + KnownTranslationFactory::enchantment_swift_sneak(), + Rarity::MYTHIC, + 0, + 0, + 3, + fn(int $level) : int => 10 * $level, + 5 + )); } protected static function register(string $name, Enchantment $member) : void{ diff --git a/src/network/mcpe/InventoryManager.php b/src/network/mcpe/InventoryManager.php index 254eff910..bd925a5c0 100644 --- a/src/network/mcpe/InventoryManager.php +++ b/src/network/mcpe/InventoryManager.php @@ -35,9 +35,12 @@ use pocketmine\block\inventory\LoomInventory; use pocketmine\block\inventory\SmithingTableInventory; use pocketmine\block\inventory\StonecutterInventory; use pocketmine\crafting\FurnaceType; +use pocketmine\data\bedrock\EnchantmentIdMap; use pocketmine\inventory\Inventory; use pocketmine\inventory\transaction\action\SlotChangeAction; use pocketmine\inventory\transaction\InventoryTransaction; +use pocketmine\item\enchantment\EnchantmentInstance; +use pocketmine\item\enchantment\EnchantOption; use pocketmine\network\mcpe\cache\CreativeInventoryCache; use pocketmine\network\mcpe\protocol\ClientboundPacket; use pocketmine\network\mcpe\protocol\ContainerClosePacket; @@ -46,7 +49,10 @@ use pocketmine\network\mcpe\protocol\ContainerSetDataPacket; use pocketmine\network\mcpe\protocol\InventoryContentPacket; use pocketmine\network\mcpe\protocol\InventorySlotPacket; use pocketmine\network\mcpe\protocol\MobEquipmentPacket; +use pocketmine\network\mcpe\protocol\PlayerEnchantOptionsPacket; use pocketmine\network\mcpe\protocol\types\BlockPosition; +use pocketmine\network\mcpe\protocol\types\Enchant; +use pocketmine\network\mcpe\protocol\types\EnchantOption as ProtocolEnchantOption; use pocketmine\network\mcpe\protocol\types\inventory\ContainerIds; use pocketmine\network\mcpe\protocol\types\inventory\ItemStack; use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; @@ -58,6 +64,7 @@ use pocketmine\player\Player; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\ObjectSet; use function array_keys; +use function array_map; use function array_search; use function count; use function get_class; @@ -103,6 +110,12 @@ class InventoryManager{ private bool $fullSyncRequested = false; + /** @var int[] network recipe ID => enchanting table option index */ + private array $enchantingTableOptions = []; + //TODO: this should be based on the total number of crafting recipes - if there are ever 100k recipes, this will + //conflict with regular recipes + private int $nextEnchantingTableOptionId = 100000; + public function __construct( private Player $player, private NetworkSession $session @@ -382,6 +395,7 @@ class InventoryManager{ throw new AssumptionFailedError("We should not have opened a new window while a window was waiting to be closed"); } $this->pendingCloseWindowId = $this->lastInventoryNetworkId; + $this->enchantingTableOptions = []; } } @@ -603,6 +617,39 @@ class InventoryManager{ $this->session->sendDataPacket(CreativeInventoryCache::getInstance()->getCache($this->player->getCreativeInventory())); } + /** + * @param EnchantOption[] $options + */ + public function syncEnchantingTableOptions(array $options) : void{ + $protocolOptions = []; + + foreach($options as $index => $option){ + $optionId = $this->nextEnchantingTableOptionId++; + $this->enchantingTableOptions[$optionId] = $index; + + $protocolEnchantments = array_map( + fn(EnchantmentInstance $e) => new Enchant(EnchantmentIdMap::getInstance()->toId($e->getType()), $e->getLevel()), + $option->getEnchantments() + ); + // We don't pay attention to the $slotFlags, $heldActivatedEnchantments and $selfActivatedEnchantments + // as everything works fine without them (perhaps these values are used somehow in the BDS). + $protocolOptions[] = new ProtocolEnchantOption( + $option->getRequiredXpLevel(), + 0, $protocolEnchantments, + [], + [], + $option->getDisplayName(), + $optionId + ); + } + + $this->session->sendDataPacket(PlayerEnchantOptionsPacket::create($protocolOptions)); + } + + public function getEnchantingTableOptionIndex(int $recipeId) : ?int{ + return $this->enchantingTableOptions[$recipeId] ?? null; + } + private function newItemStackId() : int{ return $this->nextItemStackId++; } diff --git a/src/network/mcpe/handler/ItemStackRequestExecutor.php b/src/network/mcpe/handler/ItemStackRequestExecutor.php index 10787f84b..8273068ab 100644 --- a/src/network/mcpe/handler/ItemStackRequestExecutor.php +++ b/src/network/mcpe/handler/ItemStackRequestExecutor.php @@ -23,11 +23,13 @@ declare(strict_types=1); namespace pocketmine\network\mcpe\handler; +use pocketmine\block\inventory\EnchantInventory; use pocketmine\inventory\Inventory; use pocketmine\inventory\transaction\action\CreateItemAction; use pocketmine\inventory\transaction\action\DestroyItemAction; use pocketmine\inventory\transaction\action\DropItemAction; use pocketmine\inventory\transaction\CraftingTransaction; +use pocketmine\inventory\transaction\EnchantTransaction; use pocketmine\inventory\transaction\InventoryTransaction; use pocketmine\inventory\transaction\TransactionBuilder; use pocketmine\inventory\transaction\TransactionBuilderInventory; @@ -287,7 +289,7 @@ class ItemStackRequestExecutor{ * @throws ItemStackRequestProcessException */ private function assertDoingCrafting() : void{ - if(!$this->specialTransaction instanceof CraftingTransaction){ + if(!$this->specialTransaction instanceof CraftingTransaction && !$this->specialTransaction instanceof EnchantTransaction){ if($this->specialTransaction === null){ throw new ItemStackRequestProcessException("Expected CraftRecipe or CraftRecipeAuto action to precede this action"); }else{ @@ -333,7 +335,16 @@ class ItemStackRequestExecutor{ $this->setNextCreatedItem($item, true); }elseif($action instanceof CraftRecipeStackRequestAction){ - $this->beginCrafting($action->getRecipeId(), 1); + $window = $this->player->getCurrentWindow(); + if($window instanceof EnchantInventory){ + $optionId = $this->inventoryManager->getEnchantingTableOptionIndex($action->getRecipeId()); + if($optionId !== null && ($option = $window->getOption($optionId)) !== null){ + $this->specialTransaction = new EnchantTransaction($this->player, $option, $optionId + 1); + $this->setNextCreatedItem($window->getOutput($optionId)); + } + }else{ + $this->beginCrafting($action->getRecipeId(), 1); + } }elseif($action instanceof CraftRecipeAutoStackRequestAction){ $this->beginCrafting($action->getRecipeId(), $action->getRepetitions()); }elseif($action instanceof CraftingConsumeInputStackRequestAction){ From 5afbb9d8072db2984899faf832d5c4e54ea1c7cd Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 15 Aug 2023 19:10:03 +0100 Subject: [PATCH 17/46] Allow enchanted books to be enchanted if an enchanted book is obtained via /give without enchantments, it should be able to receive enchantments in an enchanting table, exactly the same as regular books. --- src/item/VanillaItems.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/item/VanillaItems.php b/src/item/VanillaItems.php index 07ba398d5..a5d50c9db 100644 --- a/src/item/VanillaItems.php +++ b/src/item/VanillaItems.php @@ -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")); From f516c3c50203555ce34b2049bf43fa78e0793f5c Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 15 Aug 2023 19:10:48 +0100 Subject: [PATCH 18/46] EnchantCommand: ensure that books are turned into enchanted book items --- src/command/defaults/EnchantCommand.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/command/defaults/EnchantCommand.php b/src/command/defaults/EnchantCommand.php index 583bd59ec..189931b99 100644 --- a/src/command/defaults/EnchantCommand.php +++ b/src/command/defaults/EnchantCommand.php @@ -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; From e323c5dd76fa022af1c8be6d7ad82d4b9ccdd34a Mon Sep 17 00:00:00 2001 From: Dylan T Date: Wed, 16 Aug 2023 13:00:23 +0100 Subject: [PATCH 19/46] Implement pressure plate activation logic and events (#5991) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes #5936 This implements all of the basic activation logic for pressure plates. It also introduces a PressurePlateUpdateEvent, which is called in pulses when entities are standing on top of the plate and when it deactivates. Deactivation can be detected by checking if the list of activating entities is empty. --------- Co-authored-by: Javier León <58715544+JavierLeon9966@users.noreply.github.com> --- src/block/PressurePlate.php | 105 +++++++++++++++++- src/block/SimplePressurePlate.php | 16 +++ src/block/StonePressurePlate.php | 7 ++ src/block/VanillaBlocks.php | 20 +++- src/block/WeightedPressurePlate.php | 35 +++++- src/block/WeightedPressurePlateHeavy.php | 3 + src/block/WeightedPressurePlateLight.php | 3 + src/block/WoodenPressurePlate.php | 12 ++ src/event/block/PressurePlateUpdateEvent.php | 53 +++++++++ .../sound/PressurePlateActivateSound.php | 46 ++++++++ .../sound/PressurePlateDeactivateSound.php | 46 ++++++++ 11 files changed, 340 insertions(+), 6 deletions(-) create mode 100644 src/event/block/PressurePlateUpdateEvent.php create mode 100644 src/world/sound/PressurePlateActivateSound.php create mode 100644 src/world/sound/PressurePlateDeactivateSound.php diff --git a/src/block/PressurePlate.php b/src/block/PressurePlate.php index 4df0bf927..d67433a75 100644 --- a/src/block/PressurePlate.php +++ b/src/block/PressurePlate.php @@ -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); + } + } + } } diff --git a/src/block/SimplePressurePlate.php b/src/block/SimplePressurePlate.php index e4278410d..3429b9b5d 100644 --- a/src/block/SimplePressurePlate.php +++ b/src/block/SimplePressurePlate.php @@ -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 + ]; + } } diff --git a/src/block/StonePressurePlate.php b/src/block/StonePressurePlate.php index 626e6d885..5ddc5a599 100644 --- a/src/block/StonePressurePlate.php +++ b/src/block/StonePressurePlate.php @@ -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 + } } diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index cb612031f..a41c3985b 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -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)); diff --git a/src/block/WeightedPressurePlate.php b/src/block/WeightedPressurePlate.php index bdfae5082..726b31f6b 100644 --- a/src/block/WeightedPressurePlate.php +++ b/src/block/WeightedPressurePlate.php @@ -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 + ]; + } } diff --git a/src/block/WeightedPressurePlateHeavy.php b/src/block/WeightedPressurePlateHeavy.php index 390297436..9a8d1c31b 100644 --- a/src/block/WeightedPressurePlateHeavy.php +++ b/src/block/WeightedPressurePlateHeavy.php @@ -23,6 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +/** + * @deprecated + */ class WeightedPressurePlateHeavy extends WeightedPressurePlate{ } diff --git a/src/block/WeightedPressurePlateLight.php b/src/block/WeightedPressurePlateLight.php index 458c07e1a..85c13d438 100644 --- a/src/block/WeightedPressurePlateLight.php +++ b/src/block/WeightedPressurePlateLight.php @@ -23,6 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +/** + * @deprecated + */ class WeightedPressurePlateLight extends WeightedPressurePlate{ } diff --git a/src/block/WoodenPressurePlate.php b/src/block/WoodenPressurePlate.php index baaf44c2c..a629c2f1c 100644 --- a/src/block/WoodenPressurePlate.php +++ b/src/block/WoodenPressurePlate.php @@ -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; } diff --git a/src/event/block/PressurePlateUpdateEvent.php b/src/event/block/PressurePlateUpdateEvent.php new file mode 100644 index 000000000..485a3a6be --- /dev/null +++ b/src/event/block/PressurePlateUpdateEvent.php @@ -0,0 +1,53 @@ +activatingEntities; } +} diff --git a/src/world/sound/PressurePlateActivateSound.php b/src/world/sound/PressurePlateActivateSound.php new file mode 100644 index 000000000..fac24e285 --- /dev/null +++ b/src/world/sound/PressurePlateActivateSound.php @@ -0,0 +1,46 @@ +getBlockTranslator()->internalIdToNetworkId($this->block->getStateId()) + )]; + } +} diff --git a/src/world/sound/PressurePlateDeactivateSound.php b/src/world/sound/PressurePlateDeactivateSound.php new file mode 100644 index 000000000..895bb8b8a --- /dev/null +++ b/src/world/sound/PressurePlateDeactivateSound.php @@ -0,0 +1,46 @@ +getBlockTranslator()->internalIdToNetworkId($this->block->getStateId()) + )]; + } +} From beaca8bb6df5fe0d88b5ba77d5b5279b0f7222be Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 16 Aug 2023 14:51:47 +0100 Subject: [PATCH 20/46] EnchantTransaction: fixed XP level costs when minimum level is less than the XP cost this can happen and happens in vanilla too. In these cases, as much of the XP cost as possible is deducted. --- src/inventory/transaction/EnchantTransaction.php | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/inventory/transaction/EnchantTransaction.php b/src/inventory/transaction/EnchantTransaction.php index f3760e479..3d9fd7e06 100644 --- a/src/inventory/transaction/EnchantTransaction.php +++ b/src/inventory/transaction/EnchantTransaction.php @@ -56,6 +56,15 @@ class EnchantTransaction extends InventoryTransaction{ } } + /** + * The selected option might be available to a player who has enough XP levels to meet the option's minimum level, + * but not enough to pay the full cost (e.g. option costs 3 levels but requires only 1 to use). As much XP as + * possible is spent in these cases. + */ + private function getAdjustedXpCost() : int{ + return min($this->cost, $this->source->getXpManager()->getXpLevel()); + } + private function validateFiniteResources(int $lapisSpent) : void{ if($lapisSpent !== $this->cost){ throw new TransactionValidationException("Expected the amount of lapis lazuli spent to be $this->cost, but received $lapisSpent"); @@ -63,12 +72,13 @@ class EnchantTransaction extends InventoryTransaction{ $xpLevel = $this->source->getXpManager()->getXpLevel(); $requiredXpLevel = $this->option->getRequiredXpLevel(); + $actualCost = $this->getAdjustedXpCost(); 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"); + if($xpLevel < $actualCost){ + throw new TransactionValidationException("Player's XP level $xpLevel is less than the XP level cost $actualCost"); } } @@ -115,7 +125,7 @@ class EnchantTransaction extends InventoryTransaction{ parent::execute(); if($this->source->hasFiniteResources()){ - $this->source->getXpManager()->subtractXpLevels($this->cost); + $this->source->getXpManager()->subtractXpLevels($this->getAdjustedXpCost()); } $this->source->setEnchantmentSeed($this->source->generateEnchantmentSeed()); } From b2414b4c29f54543fe2a0e1b3ecbf7bc580fdbab Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 18 Aug 2023 12:33:07 +0100 Subject: [PATCH 21/46] EnchantTransaction: cleanup XP cost checking logic --- .../transaction/EnchantTransaction.php | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/inventory/transaction/EnchantTransaction.php b/src/inventory/transaction/EnchantTransaction.php index 3d9fd7e06..5ea8a997f 100644 --- a/src/inventory/transaction/EnchantTransaction.php +++ b/src/inventory/transaction/EnchantTransaction.php @@ -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{ @@ -56,15 +57,6 @@ class EnchantTransaction extends InventoryTransaction{ } } - /** - * The selected option might be available to a player who has enough XP levels to meet the option's minimum level, - * but not enough to pay the full cost (e.g. option costs 3 levels but requires only 1 to use). As much XP as - * possible is spent in these cases. - */ - private function getAdjustedXpCost() : int{ - return min($this->cost, $this->source->getXpManager()->getXpLevel()); - } - private function validateFiniteResources(int $lapisSpent) : void{ if($lapisSpent !== $this->cost){ throw new TransactionValidationException("Expected the amount of lapis lazuli spent to be $this->cost, but received $lapisSpent"); @@ -72,14 +64,12 @@ class EnchantTransaction extends InventoryTransaction{ $xpLevel = $this->source->getXpManager()->getXpLevel(); $requiredXpLevel = $this->option->getRequiredXpLevel(); - $actualCost = $this->getAdjustedXpCost(); if($xpLevel < $requiredXpLevel){ throw new TransactionValidationException("Player's XP level $xpLevel is less than the required XP level $requiredXpLevel"); } - if($xpLevel < $actualCost){ - throw new TransactionValidationException("Player's XP level $xpLevel is less than the XP level cost $actualCost"); - } + //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{ @@ -125,7 +115,9 @@ class EnchantTransaction extends InventoryTransaction{ parent::execute(); if($this->source->hasFiniteResources()){ - $this->source->getXpManager()->subtractXpLevels($this->getAdjustedXpCost()); + //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()); } From d44e0e87d0106ff003de830a1296974cbd13e8ee Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 21 Aug 2023 16:14:43 +0100 Subject: [PATCH 22/46] BaseSign: implement sign editing this was originally submitted by #6000, but considering the overcomplicated PR and the triviality of the feature, I figured it would be quicker to do it myself instead of having a bunch of back-and-forth bikeshedding over it. --- src/block/BaseSign.php | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/block/BaseSign.php b/src/block/BaseSign.php index 7f65b59f4..abb10dffe 100644 --- a/src/block/BaseSign.php +++ b/src/block/BaseSign.php @@ -159,20 +159,25 @@ abstract class BaseSign extends Transparent{ }; if($dyeColor !== null){ $color = $dyeColor->equals(DyeColor::BLACK()) ? new Color(0, 0, 0) : $dyeColor->getRgbValue(); - if($color->toARGB() === $this->text->getBaseColor()->toARGB()){ - return false; - } - - if($this->doSignChange(new SignText($this->text->getLines(), $color, $this->text->isGlowing()), $player, $item)){ + if( + $color->toARGB() !== $this->text->getBaseColor()->toARGB() && + $this->doSignChange(new SignText($this->text->getLines(), $color, $this->text->isGlowing()), $player, $item) + ){ $this->position->getWorld()->addSound($this->position, new DyeUseSound()); return true; } - }elseif($item->getTypeId() === ItemTypeIds::INK_SAC){ - return $this->changeSignGlowingState(false, $player, $item); - }elseif($item->getTypeId() === ItemTypeIds::GLOW_INK_SAC){ - return $this->changeSignGlowingState(true, $player, $item); + }elseif(match($item->getTypeId()){ + ItemTypeIds::INK_SAC => $this->changeSignGlowingState(false, $player, $item), + ItemTypeIds::GLOW_INK_SAC => $this->changeSignGlowingState(true, $player, $item), + default => false + }){ + return true; } - return false; + + //TODO: editor should not open for waxed signs + $player->openSignEditor($this->position); + + return true; } /** @@ -217,7 +222,7 @@ abstract class BaseSign extends Transparent{ } $ev = new SignChangeEvent($this, $author, new SignText(array_map(function(string $line) : string{ return TextFormat::clean($line, false); - }, $text->getLines()))); + }, $text->getLines()), $this->text->getBaseColor(), $this->text->isGlowing())); if($this->editorEntityRuntimeId === null || $this->editorEntityRuntimeId !== $author->getId()){ $ev->cancel(); } From 22778583cf8108a1007f741f86815f9d181d520a Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 21 Aug 2023 16:28:17 +0100 Subject: [PATCH 23/46] Sign: implement waxing using honeycomb --- src/block/BaseSign.php | 35 +++++++++++++++++++++++++++++++++-- src/block/tile/Sign.php | 10 +++++++++- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/block/BaseSign.php b/src/block/BaseSign.php index abb10dffe..a3556054b 100644 --- a/src/block/BaseSign.php +++ b/src/block/BaseSign.php @@ -49,6 +49,8 @@ abstract class BaseSign extends Transparent{ use WoodTypeTrait; protected SignText $text; + private bool $waxed = false; + protected ?int $editorEntityRuntimeId = null; /** @var \Closure() : Item */ @@ -69,6 +71,7 @@ abstract class BaseSign extends Transparent{ $tile = $this->position->getWorld()->getTile($this->position); if($tile instanceof TileSign){ $this->text = $tile->getText(); + $this->waxed = $tile->isWaxed(); $this->editorEntityRuntimeId = $tile->getEditorEntityRuntimeId(); } @@ -80,6 +83,7 @@ abstract class BaseSign extends Transparent{ $tile = $this->position->getWorld()->getTile($this->position); assert($tile instanceof TileSign); $tile->setText($this->text); + $tile->setWaxed($this->waxed); $tile->setEditorEntityRuntimeId($this->editorEntityRuntimeId); } @@ -147,10 +151,26 @@ abstract class BaseSign extends Transparent{ return false; } + private function wax(Player $player, Item $item) : bool{ + if($this->waxed){ + return false; + } + + $this->waxed = true; + $this->position->getWorld()->setBlock($this->position, $this); + $item->pop(); + + return true; + } + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player === null){ return false; } + if($this->waxed){ + return true; + } + $dyeColor = $item instanceof Dye ? $item->getColor() : match($item->getTypeId()){ ItemTypeIds::BONE_MEAL => DyeColor::WHITE(), ItemTypeIds::LAPIS_LAZULI => DyeColor::BLUE(), @@ -169,12 +189,12 @@ abstract class BaseSign extends Transparent{ }elseif(match($item->getTypeId()){ ItemTypeIds::INK_SAC => $this->changeSignGlowingState(false, $player, $item), ItemTypeIds::GLOW_INK_SAC => $this->changeSignGlowingState(true, $player, $item), + ItemTypeIds::HONEYCOMB => $this->wax($player, $item), default => false }){ return true; } - //TODO: editor should not open for waxed signs $player->openSignEditor($this->position); return true; @@ -193,6 +213,17 @@ abstract class BaseSign extends Transparent{ return $this; } + /** + * Returns whether the sign has been waxed using a honeycomb. If true, the sign cannot be edited by a player. + */ + public function isWaxed() : bool{ return $this->waxed; } + + /** @return $this */ + public function setWaxed(bool $waxed) : self{ + $this->waxed = $waxed; + return $this; + } + /** * Sets the runtime entity ID of the player editing this sign. Only this player will be able to edit the sign. * This is used to prevent multiple players from editing the same sign at the same time, and to prevent players @@ -223,7 +254,7 @@ abstract class BaseSign extends Transparent{ $ev = new SignChangeEvent($this, $author, new SignText(array_map(function(string $line) : string{ return TextFormat::clean($line, false); }, $text->getLines()), $this->text->getBaseColor(), $this->text->isGlowing())); - if($this->editorEntityRuntimeId === null || $this->editorEntityRuntimeId !== $author->getId()){ + if($this->waxed || $this->editorEntityRuntimeId === null || $this->editorEntityRuntimeId !== $author->getId()){ $ev->cancel(); } $ev->call(); diff --git a/src/block/tile/Sign.php b/src/block/tile/Sign.php index 047c54703..d5d314ee3 100644 --- a/src/block/tile/Sign.php +++ b/src/block/tile/Sign.php @@ -68,6 +68,8 @@ class Sign extends Spawnable{ } protected SignText $text; + private bool $waxed = false; + protected ?int $editorEntityRuntimeId = null; public function __construct(World $world, Vector3 $pos){ @@ -101,6 +103,7 @@ class Sign extends Spawnable{ } $this->text = new SignText($text); } + $this->waxed = $nbt->getByte(self::TAG_WAXED, 0) !== 0; } protected function writeSaveData(CompoundTag $nbt) : void{ @@ -113,6 +116,7 @@ class Sign extends Spawnable{ $nbt->setInt(self::TAG_TEXT_COLOR, Binary::signInt($this->text->getBaseColor()->toARGB())); $nbt->setByte(self::TAG_GLOWING_TEXT, $this->text->isGlowing() ? 1 : 0); $nbt->setByte(self::TAG_LEGACY_BUG_RESOLVE, 1); + $nbt->setByte(self::TAG_WAXED, $this->waxed ? 1 : 0); } public function getText() : SignText{ @@ -123,6 +127,10 @@ class Sign extends Spawnable{ $this->text = $text; } + public function isWaxed() : bool{ return $this->waxed; } + + public function setWaxed(bool $waxed) : void{ $this->waxed = $waxed; } + /** * Returns the entity runtime ID of the player who placed this sign. Only the player whose entity ID matches this * one may edit the sign text. @@ -153,7 +161,7 @@ class Sign extends Spawnable{ ->setByte(self::TAG_GLOWING_TEXT, 0) ->setByte(self::TAG_PERSIST_FORMATTING, 1) ); - $nbt->setByte(self::TAG_WAXED, 0); + $nbt->setByte(self::TAG_WAXED, $this->waxed ? 1 : 0); $nbt->setLong(self::TAG_LOCKED_FOR_EDITING_BY, $this->editorEntityRuntimeId ?? -1); } } From cd6abbe0bb3ffbfa2c1797db2971e98593305c38 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 21 Aug 2023 16:30:16 +0100 Subject: [PATCH 24/46] BaseSign: remove redundant condition --- src/block/BaseSign.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/block/BaseSign.php b/src/block/BaseSign.php index a3556054b..d7a22f3b8 100644 --- a/src/block/BaseSign.php +++ b/src/block/BaseSign.php @@ -254,7 +254,7 @@ abstract class BaseSign extends Transparent{ $ev = new SignChangeEvent($this, $author, new SignText(array_map(function(string $line) : string{ return TextFormat::clean($line, false); }, $text->getLines()), $this->text->getBaseColor(), $this->text->isGlowing())); - if($this->waxed || $this->editorEntityRuntimeId === null || $this->editorEntityRuntimeId !== $author->getId()){ + if($this->waxed || $this->editorEntityRuntimeId !== $author->getId()){ $ev->cancel(); } $ev->call(); From d03e4d17ec79e29745e8b94ee8b6f0da9f52e9cf Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 23 Aug 2023 14:26:17 +0100 Subject: [PATCH 25/46] Use hasHandlers() for events in player movement processing pathway this should offer a minor performance improvement. --- src/entity/HungerManager.php | 16 ++++++++++------ src/player/Player.php | 20 +++++++++++--------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/entity/HungerManager.php b/src/entity/HungerManager.php index 304dcd30b..a31855891 100644 --- a/src/entity/HungerManager.php +++ b/src/entity/HungerManager.php @@ -133,14 +133,18 @@ class HungerManager{ if(!$this->enabled){ return 0; } - $ev = new PlayerExhaustEvent($this->entity, $amount, $cause); - $ev->call(); - if($ev->isCancelled()){ - return 0.0; + $evAmount = $amount; + if(PlayerExhaustEvent::hasHandlers()){ + $ev = new PlayerExhaustEvent($this->entity, $amount, $cause); + $ev->call(); + if($ev->isCancelled()){ + return 0.0; + } + $evAmount = $ev->getAmount(); } $exhaustion = $this->getExhaustion(); - $exhaustion += $ev->getAmount(); + $exhaustion += $evAmount; while($exhaustion >= 4.0){ $exhaustion -= 4.0; @@ -159,7 +163,7 @@ class HungerManager{ } $this->setExhaustion($exhaustion); - return $ev->getAmount(); + return $evAmount; } public function getFoodTickTimer() : int{ diff --git a/src/player/Player.php b/src/player/Player.php index f62485242..d34a357ee 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -1330,18 +1330,20 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $deltaAngle = abs($this->lastLocation->yaw - $to->yaw) + abs($this->lastLocation->pitch - $to->pitch); if($delta > 0.0001 || $deltaAngle > 1.0){ - $ev = new PlayerMoveEvent($this, $from, $to); + if(PlayerMoveEvent::hasHandlers()){ + $ev = new PlayerMoveEvent($this, $from, $to); - $ev->call(); + $ev->call(); - if($ev->isCancelled()){ - $this->revertMovement($from); - return; - } + if($ev->isCancelled()){ + $this->revertMovement($from); + return; + } - if($to->distanceSquared($ev->getTo()) > 0.01){ //If plugins modify the destination - $this->teleport($ev->getTo()); - return; + if($to->distanceSquared($ev->getTo()) > 0.01){ //If plugins modify the destination + $this->teleport($ev->getTo()); + return; + } } $this->lastLocation = $to; From f4d5605de13f3f67179d4d144710e98abc556376 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 23 Aug 2023 14:35:53 +0100 Subject: [PATCH 26/46] Use hasHandlers() on more warm-hot events --- src/world/World.php | 56 ++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/src/world/World.php b/src/world/World.php index 3ff5632cb..d917a3e82 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -694,16 +694,19 @@ class World implements ChunkManager{ */ public function addSound(Vector3 $pos, Sound $sound, ?array $players = null) : void{ $players ??= $this->getViewersForPosition($pos); - $ev = new WorldSoundEvent($this, $sound, $pos, $players); - $ev->call(); + if(WorldSoundEvent::hasHandlers()){ + $ev = new WorldSoundEvent($this, $sound, $pos, $players); + $ev->call(); + if($ev->isCancelled()){ + return; + } - if($ev->isCancelled()){ - return; + $sound = $ev->getSound(); + $players = $ev->getRecipients(); } - $pk = $ev->getSound()->encode($pos); - $players = $ev->getRecipients(); + $pk = $sound->encode($pos); if(count($pk) > 0){ if($players === $this->getViewersForPosition($pos)){ foreach($pk as $e){ @@ -720,23 +723,26 @@ class World implements ChunkManager{ */ public function addParticle(Vector3 $pos, Particle $particle, ?array $players = null) : void{ $players ??= $this->getViewersForPosition($pos); - $ev = new WorldParticleEvent($this, $particle, $pos, $players); - $ev->call(); + if(WorldParticleEvent::hasHandlers()){ + $ev = new WorldParticleEvent($this, $particle, $pos, $players); + $ev->call(); + if($ev->isCancelled()){ + return; + } - if($ev->isCancelled()){ - return; + $particle = $ev->getParticle(); + $players = $ev->getRecipients(); } - $pk = $ev->getParticle()->encode($pos); - $players = $ev->getRecipients(); + $pk = $particle->encode($pos); if(count($pk) > 0){ if($players === $this->getViewersForPosition($pos)){ foreach($pk as $e){ $this->broadcastPacketToViewers($pos, $e); } }else{ - NetworkBroadcastUtils::broadcastPackets($this->filterViewersForPosition($pos, $ev->getRecipients()), $pk); + NetworkBroadcastUtils::broadcastPackets($this->filterViewersForPosition($pos, $players), $pk); } } } @@ -2517,7 +2523,9 @@ class World implements ChunkManager{ } if($oldChunk === null){ - (new ChunkLoadEvent($this, $chunkX, $chunkZ, $chunk, true))->call(); + if(ChunkLoadEvent::hasHandlers()){ + (new ChunkLoadEvent($this, $chunkX, $chunkZ, $chunk, true))->call(); + } foreach($this->getChunkListeners($chunkX, $chunkZ) as $listener){ $listener->onChunkLoaded($chunkX, $chunkZ, $chunk); @@ -2791,7 +2799,9 @@ class World implements ChunkManager{ $this->initChunk($x, $z, $chunkData); - (new ChunkLoadEvent($this, $x, $z, $this->chunks[$chunkHash], false))->call(); + if(ChunkLoadEvent::hasHandlers()){ + (new ChunkLoadEvent($this, $x, $z, $this->chunks[$chunkHash], false))->call(); + } if(!$this->isChunkInUse($x, $z)){ $this->logger->debug("Newly loaded chunk $x $z has no loaders registered, will be unloaded at next available opportunity"); @@ -2904,12 +2914,14 @@ class World implements ChunkManager{ $chunk = $this->chunks[$chunkHash] ?? null; if($chunk !== null){ - $ev = new ChunkUnloadEvent($this, $x, $z, $chunk); - $ev->call(); - if($ev->isCancelled()){ - $this->timings->doChunkUnload->stopTiming(); + if(ChunkUnloadEvent::hasHandlers()){ + $ev = new ChunkUnloadEvent($this, $x, $z, $chunk); + $ev->call(); + if($ev->isCancelled()){ + $this->timings->doChunkUnload->stopTiming(); - return false; + return false; + } } if($trySave && $this->getAutoSave()){ @@ -3366,7 +3378,9 @@ class World implements ChunkManager{ } if(($oldChunk === null || !$oldChunk->isPopulated()) && $chunk->isPopulated()){ - (new ChunkPopulateEvent($this, $x, $z, $chunk))->call(); + if(ChunkPopulateEvent::hasHandlers()){ + (new ChunkPopulateEvent($this, $x, $z, $chunk))->call(); + } foreach($this->getChunkListeners($x, $z) as $listener){ $listener->onChunkPopulated($x, $z, $chunk); From df96e023dcd2fc887bcc7d06f5ce958788840ce6 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 23 Aug 2023 14:42:50 +0100 Subject: [PATCH 27/46] Require pocketmine/nbt 1.0.0 --- composer.json | 2 +- composer.lock | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/composer.json b/composer.json index 5a875d218..5f59f64d5 100644 --- a/composer.json +++ b/composer.json @@ -44,7 +44,7 @@ "pocketmine/locale-data": "~2.19.0", "pocketmine/log": "^0.4.0", "pocketmine/math": "~1.0.0", - "pocketmine/nbt": "^0.3.2", + "pocketmine/nbt": "~1.0.0", "pocketmine/raklib": "^0.15.0", "pocketmine/raklib-ipc": "^0.2.0", "pocketmine/snooze": "^0.5.0", diff --git a/composer.lock b/composer.lock index d908bca41..c5f2018d1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "2d51c1adf069760587b6d36f9c4a5db3", + "content-hash": "9237955fd97ba7c1697d80314fa9ad6f", "packages": [ { "name": "adhocore/json-comment", @@ -521,16 +521,16 @@ }, { "name": "pocketmine/nbt", - "version": "0.3.4", + "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/pmmp/NBT.git", - "reference": "62c02464c6708b2467c1e1a2af01af09d5114eda" + "reference": "20540271cb59e04672cb163dca73366f207974f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/NBT/zipball/62c02464c6708b2467c1e1a2af01af09d5114eda", - "reference": "62c02464c6708b2467c1e1a2af01af09d5114eda", + "url": "https://api.github.com/repos/pmmp/NBT/zipball/20540271cb59e04672cb163dca73366f207974f1", + "reference": "20540271cb59e04672cb163dca73366f207974f1", "shasum": "" }, "require": { @@ -540,7 +540,7 @@ }, "require-dev": { "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "1.10.3", + "phpstan/phpstan": "1.10.25", "phpstan/phpstan-strict-rules": "^1.0", "phpunit/phpunit": "^9.5" }, @@ -557,9 +557,9 @@ "description": "PHP library for working with Named Binary Tags", "support": { "issues": "https://github.com/pmmp/NBT/issues", - "source": "https://github.com/pmmp/NBT/tree/0.3.4" + "source": "https://github.com/pmmp/NBT/tree/1.0.0" }, - "time": "2023-04-10T11:31:20+00:00" + "time": "2023-07-14T13:01:49+00:00" }, { "name": "pocketmine/netresearch-jsonmapper", From 20a41b00bacda8514e97fc48bc786edc5595d534 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 23 Aug 2023 15:24:29 +0100 Subject: [PATCH 28/46] StringToTParser: added registerAlias() --- src/utils/StringToTParser.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/utils/StringToTParser.php b/src/utils/StringToTParser.php index 0166e2a28..d98153ac1 100644 --- a/src/utils/StringToTParser.php +++ b/src/utils/StringToTParser.php @@ -58,6 +58,21 @@ abstract class StringToTParser{ $this->callbackMap[$this->reprocess($alias)] = $callback; } + /** + * Registers a new alias for an existing known alias. + */ + public function registerAlias(string $existing, string $alias) : void{ + $existingKey = $this->reprocess($existing); + if(!isset($this->callbackMap[$existingKey])){ + throw new \InvalidArgumentException("Cannot register new alias for unknown existing alias \"$existing\""); + } + $newKey = $this->reprocess($alias); + if(isset($this->callbackMap[$newKey])){ + throw new \InvalidArgumentException("Alias \"$newKey\" is already registered"); + } + $this->callbackMap[$newKey] = $this->callbackMap[$existingKey]; + } + /** * Tries to parse the specified string into a corresponding instance of T. * @phpstan-return T|null From 29fdc8b08d38ae153a932bb2820045ee37b61538 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 23 Aug 2023 15:49:31 +0100 Subject: [PATCH 29/46] Private constructor for EnchantmentHelper --- src/item/enchantment/EnchantmentHelper.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/item/enchantment/EnchantmentHelper.php b/src/item/enchantment/EnchantmentHelper.php index bd4b68963..a427eb4a4 100644 --- a/src/item/enchantment/EnchantmentHelper.php +++ b/src/item/enchantment/EnchantmentHelper.php @@ -43,6 +43,10 @@ use function round; final class EnchantmentHelper{ private const MAX_BOOKSHELF_COUNT = 15; + private function __construct(){ + //NOOP + } + /** * @param EnchantmentInstance[] $enchantments */ From d942748203c27546be6f37ec8818aa8cb40916b3 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 23 Aug 2023 15:52:49 +0100 Subject: [PATCH 30/46] Move enchanting seed generation to EnchantmentHelper --- src/entity/Human.php | 9 ++++----- src/inventory/transaction/EnchantTransaction.php | 2 +- src/item/enchantment/EnchantmentHelper.php | 9 +++++++++ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/entity/Human.php b/src/entity/Human.php index 30142b91d..7ee729655 100644 --- a/src/entity/Human.php +++ b/src/entity/Human.php @@ -37,6 +37,7 @@ use pocketmine\inventory\InventoryHolder; use pocketmine\inventory\PlayerEnderInventory; use pocketmine\inventory\PlayerInventory; use pocketmine\inventory\PlayerOffHandInventory; +use pocketmine\item\enchantment\EnchantmentHelper; use pocketmine\item\enchantment\VanillaEnchantments; use pocketmine\item\Item; use pocketmine\item\Totem; @@ -66,7 +67,6 @@ use pocketmine\network\mcpe\protocol\types\PlayerListEntry; use pocketmine\network\mcpe\protocol\types\PlayerPermissions; use pocketmine\network\mcpe\protocol\UpdateAbilitiesPacket; use pocketmine\player\Player; -use pocketmine\utils\Limits; use pocketmine\world\sound\TotemUseSound; use Ramsey\Uuid\Uuid; use Ramsey\Uuid\UuidInterface; @@ -76,7 +76,6 @@ use function array_key_exists; use function array_merge; use function array_values; use function min; -use function mt_rand; class Human extends Living implements ProjectileSource, InventoryHolder{ @@ -219,8 +218,8 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ $this->xpSeed = $seed; } - public function generateEnchantmentSeed() : int{ - return mt_rand(Limits::INT32_MIN, Limits::INT32_MAX); + public function regenerateEnchantmentSeed() : void{ + $this->xpSeed = EnchantmentHelper::generateSeed(); } public function getXpDropAmount() : int{ @@ -346,7 +345,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ if(($xpSeedTag = $nbt->getTag(self::TAG_XP_SEED)) instanceof IntTag){ $this->xpSeed = $xpSeedTag->getValue(); }else{ - $this->xpSeed = $this->generateEnchantmentSeed(); + $this->xpSeed = EnchantmentHelper::generateSeed(); } } diff --git a/src/inventory/transaction/EnchantTransaction.php b/src/inventory/transaction/EnchantTransaction.php index 5ea8a997f..7ddcd36c2 100644 --- a/src/inventory/transaction/EnchantTransaction.php +++ b/src/inventory/transaction/EnchantTransaction.php @@ -119,7 +119,7 @@ class EnchantTransaction extends InventoryTransaction{ //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()); + $this->source->regenerateEnchantmentSeed(); } protected function callExecuteEvent() : bool{ diff --git a/src/item/enchantment/EnchantmentHelper.php b/src/item/enchantment/EnchantmentHelper.php index a427eb4a4..c1e0af5bd 100644 --- a/src/item/enchantment/EnchantmentHelper.php +++ b/src/item/enchantment/EnchantmentHelper.php @@ -28,6 +28,7 @@ use pocketmine\item\enchantment\AvailableEnchantmentRegistry as EnchantmentRegis use pocketmine\item\Item; use pocketmine\item\ItemTypeIds; use pocketmine\item\VanillaItems as Items; +use pocketmine\utils\Limits; use pocketmine\utils\Random; use pocketmine\world\Position; use function abs; @@ -37,6 +38,7 @@ use function count; use function floor; use function max; use function min; +use function mt_rand; use function ord; use function round; @@ -47,6 +49,13 @@ final class EnchantmentHelper{ //NOOP } + /** + * Generates a new random seed for enchant option randomization. + */ + public static function generateSeed() : int{ + return mt_rand(Limits::INT32_MIN, Limits::INT32_MAX); + } + /** * @param EnchantmentInstance[] $enchantments */ From bf668c0f6ce7f1c3923cf862de30e783e4549607 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 23 Aug 2023 16:07:02 +0100 Subject: [PATCH 31/46] Rename EnchantHelper related stuff Perhaps this and EnchantOption should be called EnchantingHelper and EnchantingOption respectively. The terminology used is rather inconsistent, but 'enchantment' definitely isn't the right word here. --- src/block/inventory/EnchantInventory.php | 4 ++-- src/command/defaults/EnchantCommand.php | 4 ++-- src/entity/Human.php | 6 +++--- src/inventory/transaction/EnchantTransaction.php | 4 ++-- .../{EnchantmentHelper.php => EnchantHelper.php} | 7 +++++-- 5 files changed, 14 insertions(+), 11 deletions(-) rename src/item/enchantment/{EnchantmentHelper.php => EnchantHelper.php} (97%) diff --git a/src/block/inventory/EnchantInventory.php b/src/block/inventory/EnchantInventory.php index 5d7e45259..7b79ef21c 100644 --- a/src/block/inventory/EnchantInventory.php +++ b/src/block/inventory/EnchantInventory.php @@ -26,7 +26,7 @@ namespace pocketmine\block\inventory; use pocketmine\event\player\PlayerEnchantOptionsRequestEvent; use pocketmine\inventory\SimpleInventory; use pocketmine\inventory\TemporaryInventory; -use pocketmine\item\enchantment\EnchantmentHelper as Helper; +use pocketmine\item\enchantment\EnchantHelper as Helper; use pocketmine\item\enchantment\EnchantOption; use pocketmine\item\Item; use pocketmine\world\Position; @@ -52,7 +52,7 @@ class EnchantInventory extends SimpleInventory implements BlockInventory, Tempor foreach($this->viewers as $viewer){ $this->options = []; $item = $this->getInput(); - $options = Helper::getEnchantOptions($this->holder, $item, $viewer->getEnchantmentSeed()); + $options = Helper::generateOptions($this->holder, $item, $viewer->getEnchantmentSeed()); $event = new PlayerEnchantOptionsRequestEvent($viewer, $this, $options); $event->call(); diff --git a/src/command/defaults/EnchantCommand.php b/src/command/defaults/EnchantCommand.php index 189931b99..ea1ead195 100644 --- a/src/command/defaults/EnchantCommand.php +++ b/src/command/defaults/EnchantCommand.php @@ -25,7 +25,7 @@ namespace pocketmine\command\defaults; use pocketmine\command\CommandSender; use pocketmine\command\utils\InvalidCommandSyntaxException; -use pocketmine\item\enchantment\EnchantmentHelper; +use pocketmine\item\enchantment\EnchantHelper; use pocketmine\item\enchantment\EnchantmentInstance; use pocketmine\item\enchantment\StringToEnchantmentParser; use pocketmine\lang\KnownTranslationFactory; @@ -78,7 +78,7 @@ class EnchantCommand extends VanillaCommand{ } //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)]); + $enchantedItem = EnchantHelper::enchantItem($item, [new EnchantmentInstance($enchantment, $level)]); $player->getInventory()->setItemInHand($enchantedItem); self::broadcastCommandMessage($sender, KnownTranslationFactory::commands_enchant_success($player->getName())); diff --git a/src/entity/Human.php b/src/entity/Human.php index 7ee729655..01da0cf9a 100644 --- a/src/entity/Human.php +++ b/src/entity/Human.php @@ -37,7 +37,7 @@ use pocketmine\inventory\InventoryHolder; use pocketmine\inventory\PlayerEnderInventory; use pocketmine\inventory\PlayerInventory; use pocketmine\inventory\PlayerOffHandInventory; -use pocketmine\item\enchantment\EnchantmentHelper; +use pocketmine\item\enchantment\EnchantHelper; use pocketmine\item\enchantment\VanillaEnchantments; use pocketmine\item\Item; use pocketmine\item\Totem; @@ -219,7 +219,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ } public function regenerateEnchantmentSeed() : void{ - $this->xpSeed = EnchantmentHelper::generateSeed(); + $this->xpSeed = EnchantHelper::generateSeed(); } public function getXpDropAmount() : int{ @@ -345,7 +345,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ if(($xpSeedTag = $nbt->getTag(self::TAG_XP_SEED)) instanceof IntTag){ $this->xpSeed = $xpSeedTag->getValue(); }else{ - $this->xpSeed = EnchantmentHelper::generateSeed(); + $this->xpSeed = EnchantHelper::generateSeed(); } } diff --git a/src/inventory/transaction/EnchantTransaction.php b/src/inventory/transaction/EnchantTransaction.php index 7ddcd36c2..01e8043ec 100644 --- a/src/inventory/transaction/EnchantTransaction.php +++ b/src/inventory/transaction/EnchantTransaction.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace pocketmine\inventory\transaction; use pocketmine\event\player\PlayerItemEnchantEvent; -use pocketmine\item\enchantment\EnchantmentHelper; +use pocketmine\item\enchantment\EnchantHelper; use pocketmine\item\enchantment\EnchantOption; use pocketmine\item\Item; use pocketmine\item\ItemTypeIds; @@ -51,7 +51,7 @@ class EnchantTransaction extends InventoryTransaction{ throw new AssumptionFailedError("Expected that inputItem and outputItem are not null before validating output"); } - $enchantedInput = EnchantmentHelper::enchantItem($this->inputItem, $this->option->getEnchantments()); + $enchantedInput = EnchantHelper::enchantItem($this->inputItem, $this->option->getEnchantments()); if(!$this->outputItem->equalsExact($enchantedInput)){ throw new TransactionValidationException("Invalid output item"); } diff --git a/src/item/enchantment/EnchantmentHelper.php b/src/item/enchantment/EnchantHelper.php similarity index 97% rename from src/item/enchantment/EnchantmentHelper.php rename to src/item/enchantment/EnchantHelper.php index c1e0af5bd..23ac11ddb 100644 --- a/src/item/enchantment/EnchantmentHelper.php +++ b/src/item/enchantment/EnchantHelper.php @@ -42,7 +42,10 @@ use function mt_rand; use function ord; use function round; -final class EnchantmentHelper{ +/** + * Helper methods used for enchanting using the enchanting table. + */ +final class EnchantHelper{ private const MAX_BOOKSHELF_COUNT = 15; private function __construct(){ @@ -72,7 +75,7 @@ final class EnchantmentHelper{ /** * @return EnchantOption[] */ - public static function getEnchantOptions(Position $tablePos, Item $input, int $seed) : array{ + public static function generateOptions(Position $tablePos, Item $input, int $seed) : array{ if($input->isNull() || $input->hasEnchantments()){ return []; } From 1504fdca248b72c2f842cf62c9351f916fdf8baf Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 23 Aug 2023 16:14:17 +0100 Subject: [PATCH 32/46] Use 'enchanting' terminology 'enchant' just didn't feel right, being a verb. All these things pertain to the act of enchanting. This is now also consistent with CraftingTransaction etc. The ship already sailed on EnchantInventory, which will have to be renamed at a later datte. However, that was already inconsistent with 'enchanting table', so that's the odd one out here. --- src/block/inventory/EnchantInventory.php | 12 ++++++------ src/command/defaults/EnchantCommand.php | 4 ++-- src/entity/Human.php | 6 +++--- ...=> PlayerEnchantingOptionsRequestEvent.php} | 18 +++++++++--------- src/event/player/PlayerItemEnchantEvent.php | 12 ++++++------ ...ansaction.php => EnchantingTransaction.php} | 10 +++++----- ...{EnchantHelper.php => EnchantingHelper.php} | 14 +++++++------- ...{EnchantOption.php => EnchantingOption.php} | 2 +- src/network/mcpe/InventoryManager.php | 4 ++-- .../mcpe/handler/ItemStackRequestExecutor.php | 6 +++--- 10 files changed, 44 insertions(+), 44 deletions(-) rename src/event/player/{PlayerEnchantOptionsRequestEvent.php => PlayerEnchantingOptionsRequestEvent.php} (78%) rename src/inventory/transaction/{EnchantTransaction.php => EnchantingTransaction.php} (93%) rename src/item/enchantment/{EnchantHelper.php => EnchantingHelper.php} (93%) rename src/item/enchantment/{EnchantOption.php => EnchantingOption.php} (98%) diff --git a/src/block/inventory/EnchantInventory.php b/src/block/inventory/EnchantInventory.php index 7b79ef21c..b726dbedf 100644 --- a/src/block/inventory/EnchantInventory.php +++ b/src/block/inventory/EnchantInventory.php @@ -23,11 +23,11 @@ declare(strict_types=1); namespace pocketmine\block\inventory; -use pocketmine\event\player\PlayerEnchantOptionsRequestEvent; +use pocketmine\event\player\PlayerEnchantingOptionsRequestEvent; use pocketmine\inventory\SimpleInventory; use pocketmine\inventory\TemporaryInventory; -use pocketmine\item\enchantment\EnchantHelper as Helper; -use pocketmine\item\enchantment\EnchantOption; +use pocketmine\item\enchantment\EnchantingHelper as Helper; +use pocketmine\item\enchantment\EnchantingOption; use pocketmine\item\Item; use pocketmine\world\Position; use function array_values; @@ -39,7 +39,7 @@ class EnchantInventory extends SimpleInventory implements BlockInventory, Tempor public const SLOT_INPUT = 0; public const SLOT_LAPIS = 1; - /** @var EnchantOption[] $options */ + /** @var EnchantingOption[] $options */ private array $options = []; public function __construct(Position $holder){ @@ -54,7 +54,7 @@ class EnchantInventory extends SimpleInventory implements BlockInventory, Tempor $item = $this->getInput(); $options = Helper::generateOptions($this->holder, $item, $viewer->getEnchantmentSeed()); - $event = new PlayerEnchantOptionsRequestEvent($viewer, $this, $options); + $event = new PlayerEnchantingOptionsRequestEvent($viewer, $this, $options); $event->call(); if(!$event->isCancelled() && count($event->getOptions()) > 0){ $this->options = array_values($event->getOptions()); @@ -79,7 +79,7 @@ class EnchantInventory extends SimpleInventory implements BlockInventory, Tempor return $option === null ? null : Helper::enchantItem($this->getInput(), $option->getEnchantments()); } - public function getOption(int $optionId) : ?EnchantOption{ + public function getOption(int $optionId) : ?EnchantingOption{ return $this->options[$optionId] ?? null; } } diff --git a/src/command/defaults/EnchantCommand.php b/src/command/defaults/EnchantCommand.php index ea1ead195..191a146b0 100644 --- a/src/command/defaults/EnchantCommand.php +++ b/src/command/defaults/EnchantCommand.php @@ -25,7 +25,7 @@ namespace pocketmine\command\defaults; use pocketmine\command\CommandSender; use pocketmine\command\utils\InvalidCommandSyntaxException; -use pocketmine\item\enchantment\EnchantHelper; +use pocketmine\item\enchantment\EnchantingHelper; use pocketmine\item\enchantment\EnchantmentInstance; use pocketmine\item\enchantment\StringToEnchantmentParser; use pocketmine\lang\KnownTranslationFactory; @@ -78,7 +78,7 @@ class EnchantCommand extends VanillaCommand{ } //this is necessary to deal with enchanted books, which are a different item type than regular books - $enchantedItem = EnchantHelper::enchantItem($item, [new EnchantmentInstance($enchantment, $level)]); + $enchantedItem = EnchantingHelper::enchantItem($item, [new EnchantmentInstance($enchantment, $level)]); $player->getInventory()->setItemInHand($enchantedItem); self::broadcastCommandMessage($sender, KnownTranslationFactory::commands_enchant_success($player->getName())); diff --git a/src/entity/Human.php b/src/entity/Human.php index 01da0cf9a..f2c4c7a74 100644 --- a/src/entity/Human.php +++ b/src/entity/Human.php @@ -37,7 +37,7 @@ use pocketmine\inventory\InventoryHolder; use pocketmine\inventory\PlayerEnderInventory; use pocketmine\inventory\PlayerInventory; use pocketmine\inventory\PlayerOffHandInventory; -use pocketmine\item\enchantment\EnchantHelper; +use pocketmine\item\enchantment\EnchantingHelper; use pocketmine\item\enchantment\VanillaEnchantments; use pocketmine\item\Item; use pocketmine\item\Totem; @@ -219,7 +219,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ } public function regenerateEnchantmentSeed() : void{ - $this->xpSeed = EnchantHelper::generateSeed(); + $this->xpSeed = EnchantingHelper::generateSeed(); } public function getXpDropAmount() : int{ @@ -345,7 +345,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ if(($xpSeedTag = $nbt->getTag(self::TAG_XP_SEED)) instanceof IntTag){ $this->xpSeed = $xpSeedTag->getValue(); }else{ - $this->xpSeed = EnchantHelper::generateSeed(); + $this->xpSeed = EnchantingHelper::generateSeed(); } } diff --git a/src/event/player/PlayerEnchantOptionsRequestEvent.php b/src/event/player/PlayerEnchantingOptionsRequestEvent.php similarity index 78% rename from src/event/player/PlayerEnchantOptionsRequestEvent.php rename to src/event/player/PlayerEnchantingOptionsRequestEvent.php index d0448bfd8..833185f76 100644 --- a/src/event/player/PlayerEnchantOptionsRequestEvent.php +++ b/src/event/player/PlayerEnchantingOptionsRequestEvent.php @@ -27,7 +27,7 @@ use pocketmine\block\inventory\EnchantInventory; use pocketmine\event\Cancellable; use pocketmine\event\CancellableTrait; use pocketmine\event\Event; -use pocketmine\item\enchantment\EnchantOption; +use pocketmine\item\enchantment\EnchantingOption; use pocketmine\player\Player; use pocketmine\utils\Utils; use function count; @@ -36,36 +36,36 @@ use function count; * Called when a player inserts an item into an enchanting table's input slot. * The options provided by the event will be shown on the enchanting table menu. */ -class PlayerEnchantOptionsRequestEvent extends PlayerEvent implements Cancellable{ +class PlayerEnchantingOptionsRequestEvent extends PlayerEvent implements Cancellable{ use CancellableTrait; /** - * @param EnchantOption[] $options + * @param EnchantingOption[] $options */ public function __construct( Player $player, - private readonly EnchantInventory $enchantInventory, + private readonly EnchantInventory $inventory, private array $options ){ $this->player = $player; } - public function getEnchantInventory() : EnchantInventory{ - return $this->enchantInventory; + public function getInventory() : EnchantInventory{ + return $this->inventory; } /** - * @return EnchantOption[] + * @return EnchantingOption[] */ public function getOptions() : array{ return $this->options; } /** - * @param EnchantOption[] $options + * @param EnchantingOption[] $options */ public function setOptions(array $options) : void{ - Utils::validateArrayValueType($options, function(EnchantOption $_) : void{ }); + Utils::validateArrayValueType($options, function(EnchantingOption $_) : void{ }); if(($optionCount = count($options)) > 3){ throw new \LogicException("The maximum number of options for an enchanting table is 3, but $optionCount have been passed"); } diff --git a/src/event/player/PlayerItemEnchantEvent.php b/src/event/player/PlayerItemEnchantEvent.php index 9974ab40a..76151384d 100644 --- a/src/event/player/PlayerItemEnchantEvent.php +++ b/src/event/player/PlayerItemEnchantEvent.php @@ -25,8 +25,8 @@ namespace pocketmine\event\player; use pocketmine\event\Cancellable; use pocketmine\event\CancellableTrait; -use pocketmine\inventory\transaction\EnchantTransaction; -use pocketmine\item\enchantment\EnchantOption; +use pocketmine\inventory\transaction\EnchantingTransaction; +use pocketmine\item\enchantment\EnchantingOption; use pocketmine\item\Item; use pocketmine\player\Player; @@ -38,8 +38,8 @@ class PlayerItemEnchantEvent extends PlayerEvent implements Cancellable{ public function __construct( Player $player, - private readonly EnchantTransaction $transaction, - private readonly EnchantOption $option, + private readonly EnchantingTransaction $transaction, + private readonly EnchantingOption $option, private readonly Item $inputItem, private readonly Item $outputItem, private readonly int $cost @@ -50,14 +50,14 @@ class PlayerItemEnchantEvent extends PlayerEvent implements Cancellable{ /** * Returns the inventory transaction involved in this enchant event. */ - public function getTransaction() : EnchantTransaction{ + public function getTransaction() : EnchantingTransaction{ return $this->transaction; } /** * Returns the enchantment option used. */ - public function getOption() : EnchantOption{ + public function getOption() : EnchantingOption{ return $this->option; } diff --git a/src/inventory/transaction/EnchantTransaction.php b/src/inventory/transaction/EnchantingTransaction.php similarity index 93% rename from src/inventory/transaction/EnchantTransaction.php rename to src/inventory/transaction/EnchantingTransaction.php index 01e8043ec..d00df97e2 100644 --- a/src/inventory/transaction/EnchantTransaction.php +++ b/src/inventory/transaction/EnchantingTransaction.php @@ -24,8 +24,8 @@ declare(strict_types=1); namespace pocketmine\inventory\transaction; use pocketmine\event\player\PlayerItemEnchantEvent; -use pocketmine\item\enchantment\EnchantHelper; -use pocketmine\item\enchantment\EnchantOption; +use pocketmine\item\enchantment\EnchantingHelper; +use pocketmine\item\enchantment\EnchantingOption; use pocketmine\item\Item; use pocketmine\item\ItemTypeIds; use pocketmine\player\Player; @@ -33,14 +33,14 @@ use pocketmine\utils\AssumptionFailedError; use function count; use function min; -class EnchantTransaction extends InventoryTransaction{ +class EnchantingTransaction extends InventoryTransaction{ private ?Item $inputItem = null; private ?Item $outputItem = null; public function __construct( Player $source, - private readonly EnchantOption $option, + private readonly EnchantingOption $option, private readonly int $cost ){ parent::__construct($source); @@ -51,7 +51,7 @@ class EnchantTransaction extends InventoryTransaction{ throw new AssumptionFailedError("Expected that inputItem and outputItem are not null before validating output"); } - $enchantedInput = EnchantHelper::enchantItem($this->inputItem, $this->option->getEnchantments()); + $enchantedInput = EnchantingHelper::enchantItem($this->inputItem, $this->option->getEnchantments()); if(!$this->outputItem->equalsExact($enchantedInput)){ throw new TransactionValidationException("Invalid output item"); } diff --git a/src/item/enchantment/EnchantHelper.php b/src/item/enchantment/EnchantingHelper.php similarity index 93% rename from src/item/enchantment/EnchantHelper.php rename to src/item/enchantment/EnchantingHelper.php index 23ac11ddb..fb4e8f27c 100644 --- a/src/item/enchantment/EnchantHelper.php +++ b/src/item/enchantment/EnchantingHelper.php @@ -45,7 +45,7 @@ use function round; /** * Helper methods used for enchanting using the enchanting table. */ -final class EnchantHelper{ +final class EnchantingHelper{ private const MAX_BOOKSHELF_COUNT = 15; private function __construct(){ @@ -73,7 +73,7 @@ final class EnchantHelper{ } /** - * @return EnchantOption[] + * @return EnchantingOption[] */ public static function generateOptions(Position $tablePos, Item $input, int $seed) : array{ if($input->isNull() || $input->hasEnchantments()){ @@ -89,9 +89,9 @@ final class EnchantHelper{ $bottomRequiredLevel = max($baseRequiredLevel, $bookshelfCount * 2); return [ - self::createEnchantOption($random, $input, $topRequiredLevel), - self::createEnchantOption($random, $input, $middleRequiredLevel), - self::createEnchantOption($random, $input, $bottomRequiredLevel), + self::createOption($random, $input, $topRequiredLevel), + self::createOption($random, $input, $middleRequiredLevel), + self::createOption($random, $input, $bottomRequiredLevel), ]; } @@ -133,7 +133,7 @@ final class EnchantHelper{ return $bookshelfCount; } - private static function createEnchantOption(Random $random, Item $inputItem, int $requiredXpLevel) : EnchantOption{ + private static function createOption(Random $random, Item $inputItem, int $requiredXpLevel) : EnchantingOption{ $enchantingPower = $requiredXpLevel; $enchantability = $inputItem->getEnchantability(); @@ -171,7 +171,7 @@ final class EnchantHelper{ } } - return new EnchantOption($requiredXpLevel, self::getRandomOptionName($random), $resultEnchantments); + return new EnchantingOption($requiredXpLevel, self::getRandomOptionName($random), $resultEnchantments); } /** diff --git a/src/item/enchantment/EnchantOption.php b/src/item/enchantment/EnchantingOption.php similarity index 98% rename from src/item/enchantment/EnchantOption.php rename to src/item/enchantment/EnchantingOption.php index b1c3a7af5..2bedb0cc4 100644 --- a/src/item/enchantment/EnchantOption.php +++ b/src/item/enchantment/EnchantingOption.php @@ -27,7 +27,7 @@ namespace pocketmine\item\enchantment; * Represents an option on the enchanting table menu. * If selected, all the enchantments in the option will be applied to the item. */ -class EnchantOption{ +class EnchantingOption{ /** * @param EnchantmentInstance[] $enchantments diff --git a/src/network/mcpe/InventoryManager.php b/src/network/mcpe/InventoryManager.php index 8bdce489a..ebbd71146 100644 --- a/src/network/mcpe/InventoryManager.php +++ b/src/network/mcpe/InventoryManager.php @@ -39,8 +39,8 @@ use pocketmine\data\bedrock\EnchantmentIdMap; use pocketmine\inventory\Inventory; use pocketmine\inventory\transaction\action\SlotChangeAction; use pocketmine\inventory\transaction\InventoryTransaction; +use pocketmine\item\enchantment\EnchantingOption; use pocketmine\item\enchantment\EnchantmentInstance; -use pocketmine\item\enchantment\EnchantOption; use pocketmine\network\mcpe\cache\CreativeInventoryCache; use pocketmine\network\mcpe\protocol\ClientboundPacket; use pocketmine\network\mcpe\protocol\ContainerClosePacket; @@ -645,7 +645,7 @@ class InventoryManager{ } /** - * @param EnchantOption[] $options + * @param EnchantingOption[] $options */ public function syncEnchantingTableOptions(array $options) : void{ $protocolOptions = []; diff --git a/src/network/mcpe/handler/ItemStackRequestExecutor.php b/src/network/mcpe/handler/ItemStackRequestExecutor.php index 8273068ab..a36ae9f40 100644 --- a/src/network/mcpe/handler/ItemStackRequestExecutor.php +++ b/src/network/mcpe/handler/ItemStackRequestExecutor.php @@ -29,7 +29,7 @@ use pocketmine\inventory\transaction\action\CreateItemAction; use pocketmine\inventory\transaction\action\DestroyItemAction; use pocketmine\inventory\transaction\action\DropItemAction; use pocketmine\inventory\transaction\CraftingTransaction; -use pocketmine\inventory\transaction\EnchantTransaction; +use pocketmine\inventory\transaction\EnchantingTransaction; use pocketmine\inventory\transaction\InventoryTransaction; use pocketmine\inventory\transaction\TransactionBuilder; use pocketmine\inventory\transaction\TransactionBuilderInventory; @@ -289,7 +289,7 @@ class ItemStackRequestExecutor{ * @throws ItemStackRequestProcessException */ private function assertDoingCrafting() : void{ - if(!$this->specialTransaction instanceof CraftingTransaction && !$this->specialTransaction instanceof EnchantTransaction){ + if(!$this->specialTransaction instanceof CraftingTransaction && !$this->specialTransaction instanceof EnchantingTransaction){ if($this->specialTransaction === null){ throw new ItemStackRequestProcessException("Expected CraftRecipe or CraftRecipeAuto action to precede this action"); }else{ @@ -339,7 +339,7 @@ class ItemStackRequestExecutor{ if($window instanceof EnchantInventory){ $optionId = $this->inventoryManager->getEnchantingTableOptionIndex($action->getRecipeId()); if($optionId !== null && ($option = $window->getOption($optionId)) !== null){ - $this->specialTransaction = new EnchantTransaction($this->player, $option, $optionId + 1); + $this->specialTransaction = new EnchantingTransaction($this->player, $option, $optionId + 1); $this->setNextCreatedItem($window->getOutput($optionId)); } }else{ From 998fcf20dba652991b9d194f5217b6a02d155472 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 23 Aug 2023 16:52:22 +0100 Subject: [PATCH 33/46] Remove useless Cancellable from PressurePlateUpdateEvent --- src/event/block/PressurePlateUpdateEvent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/event/block/PressurePlateUpdateEvent.php b/src/event/block/PressurePlateUpdateEvent.php index 485a3a6be..eefade935 100644 --- a/src/event/block/PressurePlateUpdateEvent.php +++ b/src/event/block/PressurePlateUpdateEvent.php @@ -31,7 +31,7 @@ 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{ +final class PressurePlateUpdateEvent extends BaseBlockChangeEvent{ /** * @param Entity[] $activatingEntities */ From f4e1c31dcf4dccb9b7c517e4540ed2edd9f249c0 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 23 Aug 2023 16:52:47 +0100 Subject: [PATCH 34/46] Change some weird constant names --- src/item/enchantment/AvailableEnchantmentRegistry.php | 6 +++--- src/item/enchantment/IncompatibleEnchantmentGroups.php | 2 +- src/item/enchantment/IncompatibleEnchantmentRegistry.php | 2 +- src/item/enchantment/ItemEnchantmentTagRegistry.php | 4 ++-- src/item/enchantment/ItemEnchantmentTags.php | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/item/enchantment/AvailableEnchantmentRegistry.php b/src/item/enchantment/AvailableEnchantmentRegistry.php index af8484049..2c6f421ed 100644 --- a/src/item/enchantment/AvailableEnchantmentRegistry.php +++ b/src/item/enchantment/AvailableEnchantmentRegistry.php @@ -59,9 +59,9 @@ final class AvailableEnchantmentRegistry{ $this->register(Enchantments::SHARPNESS(), [Tags::SWORD, Tags::AXE], []); $this->register(Enchantments::KNOCKBACK(), [Tags::SWORD], []); $this->register(Enchantments::FIRE_ASPECT(), [Tags::SWORD], []); - $this->register(Enchantments::EFFICIENCY(), [Tags::DIG_TOOLS], [Tags::SHEARS]); - $this->register(Enchantments::FORTUNE(), [Tags::DIG_TOOLS], []); - $this->register(Enchantments::SILK_TOUCH(), [Tags::DIG_TOOLS], [Tags::SHEARS]); + $this->register(Enchantments::EFFICIENCY(), [Tags::BLOCK_TOOLS], [Tags::SHEARS]); + $this->register(Enchantments::FORTUNE(), [Tags::BLOCK_TOOLS], []); + $this->register(Enchantments::SILK_TOUCH(), [Tags::BLOCK_TOOLS], [Tags::SHEARS]); $this->register( Enchantments::UNBREAKING(), [Tags::ARMOR, Tags::WEAPONS, Tags::FISHING_ROD], diff --git a/src/item/enchantment/IncompatibleEnchantmentGroups.php b/src/item/enchantment/IncompatibleEnchantmentGroups.php index ed1141bee..74574562c 100644 --- a/src/item/enchantment/IncompatibleEnchantmentGroups.php +++ b/src/item/enchantment/IncompatibleEnchantmentGroups.php @@ -30,5 +30,5 @@ namespace pocketmine\item\enchantment; final class IncompatibleEnchantmentGroups{ public const PROTECTION = "protection"; public const BOW_INFINITE = "bow_infinite"; - public const DIG_DROP = "dig_drop"; + public const BLOCK_DROPS = "block_drops"; } diff --git a/src/item/enchantment/IncompatibleEnchantmentRegistry.php b/src/item/enchantment/IncompatibleEnchantmentRegistry.php index 8dce4777e..629a15970 100644 --- a/src/item/enchantment/IncompatibleEnchantmentRegistry.php +++ b/src/item/enchantment/IncompatibleEnchantmentRegistry.php @@ -46,7 +46,7 @@ final class IncompatibleEnchantmentRegistry{ private function __construct(){ $this->register(Groups::PROTECTION, [Enchantments::PROTECTION(), Enchantments::FIRE_PROTECTION(), Enchantments::BLAST_PROTECTION(), Enchantments::PROJECTILE_PROTECTION()]); $this->register(Groups::BOW_INFINITE, [Enchantments::INFINITY(), Enchantments::MENDING()]); - $this->register(Groups::DIG_DROP, [Enchantments::FORTUNE(), Enchantments::SILK_TOUCH()]); + $this->register(Groups::BLOCK_DROPS, [Enchantments::FORTUNE(), Enchantments::SILK_TOUCH()]); } /** diff --git a/src/item/enchantment/ItemEnchantmentTagRegistry.php b/src/item/enchantment/ItemEnchantmentTagRegistry.php index 9c607f9d2..210cd8e86 100644 --- a/src/item/enchantment/ItemEnchantmentTagRegistry.php +++ b/src/item/enchantment/ItemEnchantmentTagRegistry.php @@ -56,7 +56,7 @@ final class ItemEnchantmentTagRegistry{ $this->register(Tags::CROSSBOW); $this->register(Tags::SHEARS); $this->register(Tags::FLINT_AND_STEEL); - $this->register(Tags::DIG_TOOLS, [Tags::AXE, Tags::PICKAXE, Tags::SHOVEL, Tags::HOE]); + $this->register(Tags::BLOCK_TOOLS, [Tags::AXE, Tags::PICKAXE, Tags::SHOVEL, Tags::HOE]); $this->register(Tags::FISHING_ROD); $this->register(Tags::CARROT_ON_STICK); $this->register(Tags::COMPASS); @@ -68,7 +68,7 @@ final class ItemEnchantmentTagRegistry{ Tags::TRIDENT, Tags::BOW, Tags::CROSSBOW, - Tags::DIG_TOOLS, + Tags::BLOCK_TOOLS, ]); } diff --git a/src/item/enchantment/ItemEnchantmentTags.php b/src/item/enchantment/ItemEnchantmentTags.php index 485cb5d0c..50abf8db3 100644 --- a/src/item/enchantment/ItemEnchantmentTags.php +++ b/src/item/enchantment/ItemEnchantmentTags.php @@ -42,7 +42,7 @@ final class ItemEnchantmentTags{ public const CROSSBOW = "crossbow"; public const SHEARS = "shears"; public const FLINT_AND_STEEL = "flint_and_steel"; - public const DIG_TOOLS = "dig_tools"; + public const BLOCK_TOOLS = "block_tools"; public const AXE = "axe"; public const PICKAXE = "pickaxe"; public const SHOVEL = "shovel"; From 923f7561fbcebed82955df80ed76a6ee6e384345 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 23 Aug 2023 16:53:09 +0100 Subject: [PATCH 35/46] Enchantment: added @see tags to @deprecated methods --- src/item/enchantment/Enchantment.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/item/enchantment/Enchantment.php b/src/item/enchantment/Enchantment.php index 22c0cdb01..948f4648b 100644 --- a/src/item/enchantment/Enchantment.php +++ b/src/item/enchantment/Enchantment.php @@ -83,6 +83,7 @@ class Enchantment{ * Returns a bitset indicating what item types can have this item applied from an enchanting table. * * @deprecated + * @see AvailableEnchantmentRegistry::getPrimaryItemTags() */ public function getPrimaryItemFlags() : int{ return $this->primaryItemFlags; @@ -93,6 +94,7 @@ class Enchantment{ * an anvil. * * @deprecated + * @see AvailableEnchantmentRegistry::getSecondaryItemTags() */ public function getSecondaryItemFlags() : int{ return $this->secondaryItemFlags; @@ -102,6 +104,7 @@ class Enchantment{ * Returns whether this enchantment can apply to the item type from an enchanting table. * * @deprecated + * @see AvailableEnchantmentRegistry */ public function hasPrimaryItemType(int $flag) : bool{ return ($this->primaryItemFlags & $flag) !== 0; @@ -111,6 +114,7 @@ class Enchantment{ * Returns whether this enchantment can apply to the item type from an anvil, if it is not a primary item. * * @deprecated + * @see AvailableEnchantmentRegistry */ public function hasSecondaryItemType(int $flag) : bool{ return ($this->secondaryItemFlags & $flag) !== 0; From 8814d06dfd0ce9702c784c95aff1adb87349f985 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 23 Aug 2023 17:00:18 +0100 Subject: [PATCH 36/46] Fix CS --- src/event/block/PressurePlateUpdateEvent.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/event/block/PressurePlateUpdateEvent.php b/src/event/block/PressurePlateUpdateEvent.php index eefade935..e6f2e92ba 100644 --- a/src/event/block/PressurePlateUpdateEvent.php +++ b/src/event/block/PressurePlateUpdateEvent.php @@ -25,7 +25,6 @@ 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. From c8100480ac494562fff12bbe905d73be109e1f12 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 23 Aug 2023 17:09:34 +0100 Subject: [PATCH 37/46] Release 5.5.0-BETA1 --- changelogs/5.5-beta.md | 156 +++++++++++++++++++++++++++++++++++++++++ src/VersionInfo.php | 6 +- 2 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 changelogs/5.5-beta.md diff --git a/changelogs/5.5-beta.md b/changelogs/5.5-beta.md new file mode 100644 index 000000000..efd1c822c --- /dev/null +++ b/changelogs/5.5-beta.md @@ -0,0 +1,156 @@ +# 5.5.0-BETA1 +Released 23rd August 2023. + +**For Minecraft: Bedrock Edition 1.20.0** + +This is a minor feature release, including performance improvements, new API methods, and new gameplay features. + +**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace. +Do not update plugin minimum API versions unless you need new features added in this release. + +**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.** +Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly. + +## Dependencies +- Updated `pocketmine/math` dependency to [`1.0.0`](https://github.com/pmmp/Math/releases/tag/1.0.0). +- Updated `pocketmine/nbt` dependency to [`1.0.0`](https://github.com/pmmp/NBT/releases/tag/1.0.0). + +## Performance +- Some events are now no longer fired if no handlers are registered. + - This improves performance by avoiding unnecessary object allocations and function calls. + - Events such as `DataPacketReceiveEvent`, `DataPacketSendEvent` and `PlayerMoveEvent` are optimized away almost completely by this change, offering some much-needed performance gains. +- Significantly improved performance of small moving entities, such as dropped items. + - This was achieved by a combination of changes, which together improved observed performance with 2000 item entities moving in water by 30-40%. + - The benefit of this will be most noticeable in SkyBlock servers, where large cactus farms can generate thousands of dropped items. +- `World->getCollisionBoxes()` now uses an improved search method, which reduces the work done by the function by almost 90% for small entities. + - This improves performance of collision detection for small entities, such as dropped items. + +## Gameplay +### General +- Implemented enchanting using an enchanting table (yes, finally!) + - Thanks to [@S3v3Nice](https://github.com/S3v3Nice) for investing lots of time and effort into developing this. + - Since this feature is quite complex, it's possible there may be bugs. Please be vigilant and report any issues you find. + +### Blocks +- The following new blocks have been implemented: + - Pink Petals +- Pressure plates are now functional, in the sense that they react when entities stand on them and perform the correct logic. + - Note that since redstone is not yet implemented, pressure plates do not activate any redstone devices, similar to buttons and levers. +- Signs can now be edited by right-clicking them. +- Signs can now be waxed using a honeycomb, which prevents them from being edited. + +### Items +- The following new items have been implemented: + - Enchanted Book + +## API +### `pocketmine\block` +- The following new API methods have been added: + - `public Block->getEnchantmentTags() : list` returns a list of strings indicating which types of enchantment can be applied to the block when in item form + - `public BlockTypeInfo->getEnchantmentTags() : list` + - `protected PressurePlate->getActivationBox() : AxisAlignedBB` - returns the AABB entities must intersect with in order to activate the pressure plate (not the same as the visual shape) + - `protected PressurePlate->hasOutputSignal() : bool` - returns whether the pressure plate has an output signal - this should be implemented by subclasses + - `protected PressurePlate->calculatePlateState() : array{Block, ?bool}` - returns the state the pressure plate will change to if the given list of entities are standing on it, and a bool indicating whether the plate activated or deactivated this tick + - `protected PressurePlate->filterIrrelevantEntities(list $entities) : list` - returns the given list filtered of entities that don't affect the plate's state (e.g. dropped items don't affect stone pressure plates) + - `public BaseSign->isWaxed() : bool` + - `public BaseSign->setWaxed(bool $waxed) : $this` + - `public inventory\EnchantInventory->getInput() : Item` + - `public inventory\EnchantInventory->getLapis() : Item` + - `public inventory\EnchantInventory->getOutput(int $optionId) : ?Item` - returns the item that would be produced if the input item was enchanted with the selected option, or `null` if the option is invalid + - `public inventory\EnchantInventory->getOption(int $optionId) : EnchantOption` - returns the enchanting option at the given index +- The following API methods have signature changes: + - `BlockTypeInfo->__construct()` now accepts an optional `list $enchantmentTags` parameter + - `PressurePlate->__construct()` now accepts an optional `int $deactivationDelayTicks` parameter + - `WeightedPressurePlate->__construct()` now accepts optional `int $deactivationDelayTicks` and `float $signalStrengthFactor` parameters + - `SimplePressurePlate->__construct()` now accepts an optional `int $deactivationDelayTicks` parameter +- The following new classes have been added: + - `PinkPetals` + - `utils\BlockEventHelper` - provides helper methods for calling block-related events +- The following classes have been deprecated: + - `WeightedPressurePlateLight` + - `WeightedPressurePlateHeavy` + +### `pocketmine\entity` +- The following new API methods have been added: + - `public Human->getEnchantmentSeed() : int` - returns the current seed used to randomize options shown on the enchanting table for this human + - `public Human->setEnchantmentSeed(int $seed) : void` + - `public Human->regenerateEnchantmentSeed() : void` - returns a new randomly generated seed which can be set with `setEnchantmentSeed()` + +### `pocketmine\event` +- The following new classes have been added: + - `block\FarmlandHydrationChangeEvent` - called when farmland is hydrated or dehydrated + - `block\PressurePlateUpdateEvent` - called when a pressure plate is activated or changes its power output + - `player\PlayerEnchantingOptionsRequestEvent` - called when a player puts an item to be enchanted into an enchanting table, to allow plugins to modify the enchanting options shown + - `player\PlayerItemEnchantEvent` - called when a player enchants an item in an enchanting table + - `world\WorldDifficultyChangeEvent` - called when a world's difficulty is changed +- The following new API methods have been added: + - `public static Event::hasHandlers() : bool` - returns whether the event class has any registered handlers - used like `SomeEvent::hasHandlers()` + - `public HandlerListManager->getHandlersFor(class-string $event) : list` - returns a list of all registered listeners for the given event class, using cache if available + +### `pocketmine\inventory\transaction` +- The following new classes have been added: + - `EnchantingTransaction` - used when a player enchants an item in an enchanting table + +### `pocketmine\item` +- The following new API methods have been added: + - `public Armor->getMaterial() : ArmorMaterial` - returns an object containing properties shared by all items of the same armor material + - `public ArmorTypeInfo->getMaterial() : ArmorMaterial` + - `public Item->getEnchantability() : int` - returns the enchantability value of the item - higher values increase the chance of more powerful enchantments being offered by an enchanting table + - `public Item->getEnchantmentTags() : list` - returns a list of strings indicating which types of enchantment can be applied to the item + - `public ToolTier->getEnchantability() : int` +- The following API methods have signature changes: + - `Item->__construct()` now accepts an optional `list $enchantmentTags` parameter + - `ArmorTypeInfo->__construct()` now accepts an optional `?ArmorMaterial $material` parameter +- The following new classes have been added: + - `ArmorMaterial` - container for shared armor properties + - `VanillaArmorMaterials` - all vanilla armor materials + - `EnchantedBook` - represents an enchanted book item + +### `pocketmine\item\enchantment` +- The following new classes have been added: + - `AvailableEnchantmentRegistry` - enchantments to be displayed on the enchanting table are selected from here - custom enchantments may be added + - `EnchantingHelper` - static class containing various helper methods for enchanting tables + - `EnchantingOption` - represents an option on the enchanting table menu + - `IncompatibleEnchantmentGroups` - list of constants naming groups of enchantments that are incompatible with each other - custom enchantments may be added using these group names to make them incompatible with existing enchantments in the same group + - `IncompatibleEnchantmentRegistry` - manages which enchantments are considered incompatible with each other - custom enchantments may be added using existing group names to make them incompatible with existing enchantments in the same group, or to entirely new groups + - `ItemEnchantmentTagRegistry` - manages item enchantment compatibility tags and which tags include which other tags + - `ItemEnchantmentTags` - list of constants naming item types for enchantment compatibility checks +- The following classes have been deprecated + - `ItemFlags` +- The following API methods have been added: + - `public Enchantment->isCompatibleWith(Enchantment $other) : bool` + - `public Enchantment->getMinEnchantingPower()` - returns the minimum enchanting power (derived from enchantability and number of bookshelves) needed to allow this enchantment to show on the enchanting table with a given level + - `public Enchantment->getMaxEnchantingPower()` - upper limit of enchanting power for this enchantment to be offered on the enchanting table with a given level +- The following API methods have signature changes: + - `Enchantment->__construct()` now accepts optional `(\Closure(int $level) : int)|null $minEnchantingPower` and `int $enchantingPowerRange` parameters + - `Enchantment->__construct()` parameters `$primaryItemFlags` and `$secondaryItemFlags` are now deprecated and no longer used + - `ProtectionEnchantment->__construct()` has extra parameters to reflect `Enchantment->__construct()` changes +- The following API methods have been deprecated: + - `Enchantment->getPrimaryItemFlags()` - use API methods provided by `AvailableEnchantmentRegistry` instead + - `Enchantment->getSecondaryItemFlags()` - use API methods provided by `AvailableEnchantmentRegistry` instead + - `Enchantment->hasPrimaryItemType()` + - `Enchantment->hasSecondaryItemType()` + +### `pocketmine\plugin` +- The following new API methods have been added: + - `public PluginBase->getResourcePath(string $filename) : string` - returns a URI to an embedded resource file that can be used with `file_get_contents()` and similar functions + - `public PluginBase->getResourceFolder() : string` - returns a URI to the plugin's folder of embedded resources +- The following API methods have been deprecated: + - `PluginBase->getResource()` - prefer using `getResourcePath()` with `file_get_contents()` or other PHP built-in functions instead + +### `pocketmine\resourcepacks` +- The following new API methods have been added: + - `public ResourcePackManager->setResourcePacksRequired(bool $value) : void` - sets whether players must accept resource packs in order to join + +### `pocketmine\world\generator` +- The following new API methods have been added: + - `public GeneratorManager->addAlias(string $name, string $alias) : void` - allows registering a generator alias without copying the generator registration parameters + +### `pocketmine\world\sound` +- The following new classes have been added: +- `PressurePlateActivateSound` +- `PressurePlateDeactivateSound` + +### `pocketmine\utils` +- The following new API methods have been added: + - `public StringToTParser->registerAlias(string $existing, string $alias) : void` - allows registering a string alias without copying registration parameters diff --git a/src/VersionInfo.php b/src/VersionInfo.php index bec9638ea..80d6ecaf6 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,9 +31,9 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.4.3"; - public const IS_DEVELOPMENT_BUILD = true; - public const BUILD_CHANNEL = "stable"; + public const BASE_VERSION = "5.5.0-BETA1"; + public const IS_DEVELOPMENT_BUILD = false; + public const BUILD_CHANNEL = "beta"; /** * PocketMine-MP-specific version ID for world data. Used to determine what fixes need to be applied to old world From ce53a221a5cd0c735206b5084a0ee08bba480ced Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 23 Aug 2023 17:09:34 +0100 Subject: [PATCH 38/46] 5.5.0-BETA2 is next --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 80d6ecaf6..f792fe0a4 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.5.0-BETA1"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.5.0-BETA2"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "beta"; /** From c7a311c17a8ec25ae31ca0ec7cb81602714cfc3b Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 23 Aug 2023 17:14:56 +0100 Subject: [PATCH 39/46] COPILOT --- changelogs/5.5-beta.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/5.5-beta.md b/changelogs/5.5-beta.md index efd1c822c..84153c279 100644 --- a/changelogs/5.5-beta.md +++ b/changelogs/5.5-beta.md @@ -1,7 +1,7 @@ # 5.5.0-BETA1 Released 23rd August 2023. -**For Minecraft: Bedrock Edition 1.20.0** +**For Minecraft: Bedrock Edition 1.20.10** This is a minor feature release, including performance improvements, new API methods, and new gameplay features. From b56f1b679ea49aab81222e08a7d36f97d95a895e Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 25 Aug 2023 12:30:54 +0100 Subject: [PATCH 40/46] Deduplicate a bunch of repeated type ID map code --- build/php | 2 +- src/data/bedrock/DyeColorIdMap.php | 27 ++----- src/data/bedrock/EffectIdMap.php | 34 +------- src/data/bedrock/EnchantmentIdMap.php | 31 +------- src/data/bedrock/IntSaveIdMapTrait.php | 82 ++++++++++++++++++++ src/data/bedrock/MedicineTypeIdMap.php | 30 +------ src/data/bedrock/MobHeadTypeIdMap.php | 30 +------ src/data/bedrock/MushroomBlockTypeIdMap.php | 29 +------ src/data/bedrock/NoteInstrumentIdMap.php | 30 +------ src/data/bedrock/PotionTypeIdMap.php | 30 +------ src/data/bedrock/SuspiciousStewTypeIdMap.php | 30 +------ 11 files changed, 104 insertions(+), 251 deletions(-) create mode 100644 src/data/bedrock/IntSaveIdMapTrait.php diff --git a/build/php b/build/php index a053f65e1..9f984a1dc 160000 --- a/build/php +++ b/build/php @@ -1 +1 @@ -Subproject commit a053f65e1897e432478229071383fe1ba16032c3 +Subproject commit 9f984a1dc154603a27543ecdcebc6d6a5871f425 diff --git a/src/data/bedrock/DyeColorIdMap.php b/src/data/bedrock/DyeColorIdMap.php index 35db72c3e..0d10edad5 100644 --- a/src/data/bedrock/DyeColorIdMap.php +++ b/src/data/bedrock/DyeColorIdMap.php @@ -29,18 +29,10 @@ use pocketmine\utils\SingletonTrait; final class DyeColorIdMap{ use SingletonTrait; - - /** - * @var DyeColor[] - * @phpstan-var array - */ - private array $idToEnum = []; - - /** - * @var int[] - * @phpstan-var array - */ - private array $enumToId = []; + /** @phpstan-use IntSaveIdMapTrait */ + use IntSaveIdMapTrait { + register as registerInt; + } /** * @var DyeColor[] @@ -74,16 +66,11 @@ final class DyeColorIdMap{ } private function register(int $id, string $itemId, DyeColor $color) : void{ - $this->idToEnum[$id] = $color; - $this->enumToId[$color->id()] = $id; + $this->registerInt($id, $color); $this->itemIdToEnum[$itemId] = $color; $this->enumToItemId[$color->id()] = $itemId; } - public function toId(DyeColor $color) : int{ - return $this->enumToId[$color->id()]; //TODO: is it possible for this to be missing? - } - public function toInvertedId(DyeColor $color) : int{ return ~$this->toId($color) & 0xf; } @@ -92,10 +79,6 @@ final class DyeColorIdMap{ return $this->enumToItemId[$color->id()]; } - public function fromId(int $id) : ?DyeColor{ - return $this->idToEnum[$id] ?? null; - } - public function fromInvertedId(int $id) : ?DyeColor{ return $this->fromId(~$id & 0xf); } diff --git a/src/data/bedrock/EffectIdMap.php b/src/data/bedrock/EffectIdMap.php index 6dce86d9b..6efa10b41 100644 --- a/src/data/bedrock/EffectIdMap.php +++ b/src/data/bedrock/EffectIdMap.php @@ -31,18 +31,8 @@ use function spl_object_id; final class EffectIdMap{ use SingletonTrait; - - /** - * @var Effect[] - * @phpstan-var array - */ - private array $idToEffect = []; - - /** - * @var int[] - * @phpstan-var array - */ - private array $effectToId = []; + /** @phpstan-use IntSaveIdMapTrait */ + use IntSaveIdMapTrait; private function __construct(){ $this->register(EffectIds::SPEED, VanillaEffects::SPEED()); @@ -76,24 +66,4 @@ final class EffectIdMap{ //TODO: VILLAGE_HERO $this->register(EffectIds::DARKNESS, VanillaEffects::DARKNESS()); } - - //TODO: not a big fan of the code duplication here :( - - public function register(int $mcpeId, Effect $effect) : void{ - $this->idToEffect[$mcpeId] = $effect; - $this->effectToId[spl_object_id($effect)] = $mcpeId; - } - - public function fromId(int $id) : ?Effect{ - //we might not have all the effect IDs registered - return $this->idToEffect[$id] ?? null; - } - - public function toId(Effect $effect) : int{ - if(!array_key_exists(spl_object_id($effect), $this->effectToId)){ - //this should never happen, so we treat it as an exceptional condition - throw new \InvalidArgumentException("Effect does not have a mapped ID"); - } - return $this->effectToId[spl_object_id($effect)]; - } } diff --git a/src/data/bedrock/EnchantmentIdMap.php b/src/data/bedrock/EnchantmentIdMap.php index 1d974ed6e..c06080e3e 100644 --- a/src/data/bedrock/EnchantmentIdMap.php +++ b/src/data/bedrock/EnchantmentIdMap.php @@ -34,17 +34,8 @@ use function spl_object_id; */ final class EnchantmentIdMap{ use SingletonTrait; - - /** - * @var Enchantment[] - * @phpstan-var array - */ - private array $idToEnch = []; - /** - * @var int[] - * @phpstan-var array - */ - private array $enchToId = []; + /** @phpstan-use IntSaveIdMapTrait */ + use IntSaveIdMapTrait; private function __construct(){ $this->register(EnchantmentIds::PROTECTION, VanillaEnchantments::PROTECTION()); @@ -77,22 +68,4 @@ final class EnchantmentIdMap{ $this->register(EnchantmentIds::SWIFT_SNEAK, VanillaEnchantments::SWIFT_SNEAK()); } - - public function register(int $mcpeId, Enchantment $enchantment) : void{ - $this->idToEnch[$mcpeId] = $enchantment; - $this->enchToId[spl_object_id($enchantment)] = $mcpeId; - } - - public function fromId(int $id) : ?Enchantment{ - //we might not have all the enchantment IDs registered - return $this->idToEnch[$id] ?? null; - } - - public function toId(Enchantment $enchantment) : int{ - if(!array_key_exists(spl_object_id($enchantment), $this->enchToId)){ - //this should never happen, so we treat it as an exceptional condition - throw new \InvalidArgumentException("Enchantment does not have a mapped ID"); - } - return $this->enchToId[spl_object_id($enchantment)]; - } } diff --git a/src/data/bedrock/IntSaveIdMapTrait.php b/src/data/bedrock/IntSaveIdMapTrait.php new file mode 100644 index 000000000..041dfa9e6 --- /dev/null +++ b/src/data/bedrock/IntSaveIdMapTrait.php @@ -0,0 +1,82 @@ + + */ + private array $idToEnum = []; + + /** + * @var int[] + * @phpstan-var array + */ + private array $enumToId = []; + + /** + * @phpstan-param TObject $enum + */ + protected function getRuntimeId(object $enum) : int{ + //this is fine for enums and non-cloning object registries + return spl_object_id($enum); + } + + /** + * @phpstan-param TObject $enum + */ + public function register(int $saveId, object $enum) : void{ + $this->idToEnum[$saveId] = $enum; + $this->enumToId[$this->getRuntimeId($enum)] = $saveId; + } + + /** + * @phpstan-return TObject|null + */ + public function fromId(int $id) : ?object{ + //we might not have all the effect IDs registered + return $this->idToEnum[$id] ?? null; + } + + /** + * @phpstan-param TObject $enum + */ + public function toId(object $enum) : int{ + $runtimeId = $this->getRuntimeId($enum); + if(!array_key_exists($runtimeId, $this->enumToId)){ + //this should never happen, so we treat it as an exceptional condition + throw new \InvalidArgumentException("Object does not have a mapped save ID"); + } + return $this->enumToId[$runtimeId]; + } +} diff --git a/src/data/bedrock/MedicineTypeIdMap.php b/src/data/bedrock/MedicineTypeIdMap.php index a85dbb7a8..2d9deb380 100644 --- a/src/data/bedrock/MedicineTypeIdMap.php +++ b/src/data/bedrock/MedicineTypeIdMap.php @@ -28,18 +28,8 @@ use pocketmine\utils\SingletonTrait; final class MedicineTypeIdMap{ use SingletonTrait; - - /** - * @var MedicineType[] - * @phpstan-var array - */ - private array $idToEnum = []; - - /** - * @var int[] - * @phpstan-var array - */ - private array $enumToId = []; + /** @phpstan-use IntSaveIdMapTrait */ + use IntSaveIdMapTrait; private function __construct(){ $this->register(MedicineTypeIds::ANTIDOTE, MedicineType::ANTIDOTE()); @@ -47,20 +37,4 @@ final class MedicineTypeIdMap{ $this->register(MedicineTypeIds::EYE_DROPS, MedicineType::EYE_DROPS()); $this->register(MedicineTypeIds::TONIC, MedicineType::TONIC()); } - - private function register(int $id, MedicineType $type) : void{ - $this->idToEnum[$id] = $type; - $this->enumToId[$type->id()] = $id; - } - - public function fromId(int $id) : ?MedicineType{ - return $this->idToEnum[$id] ?? null; - } - - public function toId(MedicineType $type) : int{ - if(!isset($this->enumToId[$type->id()])){ - throw new \InvalidArgumentException("Type does not have a mapped ID"); - } - return $this->enumToId[$type->id()]; - } } diff --git a/src/data/bedrock/MobHeadTypeIdMap.php b/src/data/bedrock/MobHeadTypeIdMap.php index 99213fa46..b99b1097b 100644 --- a/src/data/bedrock/MobHeadTypeIdMap.php +++ b/src/data/bedrock/MobHeadTypeIdMap.php @@ -28,18 +28,8 @@ use pocketmine\utils\SingletonTrait; final class MobHeadTypeIdMap{ use SingletonTrait; - - /** - * @var MobHeadType[] - * @phpstan-var array - */ - private array $idToEnum = []; - - /** - * @var int[] - * @phpstan-var array - */ - private array $enumToId = []; + /** @phpstan-use IntSaveIdMapTrait */ + use IntSaveIdMapTrait; private function __construct(){ $this->register(0, MobHeadType::SKELETON()); @@ -50,20 +40,4 @@ final class MobHeadTypeIdMap{ $this->register(5, MobHeadType::DRAGON()); $this->register(6, MobHeadType::PIGLIN()); } - - private function register(int $id, MobHeadType $type) : void{ - $this->idToEnum[$id] = $type; - $this->enumToId[$type->id()] = $id; - } - - public function fromId(int $id) : ?MobHeadType{ - return $this->idToEnum[$id] ?? null; - } - - public function toId(MobHeadType $type) : int{ - if(!isset($this->enumToId[$type->id()])){ - throw new \InvalidArgumentException("Type does not have a mapped ID"); - } - return $this->enumToId[$type->id()]; - } } diff --git a/src/data/bedrock/MushroomBlockTypeIdMap.php b/src/data/bedrock/MushroomBlockTypeIdMap.php index 2eec17549..5e195ebb2 100644 --- a/src/data/bedrock/MushroomBlockTypeIdMap.php +++ b/src/data/bedrock/MushroomBlockTypeIdMap.php @@ -30,17 +30,8 @@ use function array_key_exists; final class MushroomBlockTypeIdMap{ use SingletonTrait; - - /** - * @var MushroomBlockType[] - * @phpstan-var array - */ - private array $idToEnum = []; - /** - * @var int[] - * @phpstan-var array - */ - private array $enumToId = []; + /** @phpstan-use IntSaveIdMapTrait */ + use IntSaveIdMapTrait; public function __construct(){ $this->register(LegacyMeta::MUSHROOM_BLOCK_ALL_PORES, MushroomBlockType::PORES()); @@ -55,20 +46,4 @@ final class MushroomBlockTypeIdMap{ $this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHEAST_CORNER, MushroomBlockType::CAP_SOUTHEAST()); $this->register(LegacyMeta::MUSHROOM_BLOCK_ALL_CAP, MushroomBlockType::ALL_CAP()); } - - public function register(int $id, MushroomBlockType $type) : void{ - $this->idToEnum[$id] = $type; - $this->enumToId[$type->id()] = $id; - } - - public function fromId(int $id) : ?MushroomBlockType{ - return $this->idToEnum[$id] ?? null; - } - - public function toId(MushroomBlockType $type) : int{ - if(!array_key_exists($type->id(), $this->enumToId)){ - throw new \InvalidArgumentException("Mushroom block type does not have a mapped ID"); //this should never happen - } - return $this->enumToId[$type->id()]; - } } diff --git a/src/data/bedrock/NoteInstrumentIdMap.php b/src/data/bedrock/NoteInstrumentIdMap.php index 0b8a43735..22d7e5cb1 100644 --- a/src/data/bedrock/NoteInstrumentIdMap.php +++ b/src/data/bedrock/NoteInstrumentIdMap.php @@ -28,18 +28,8 @@ use pocketmine\world\sound\NoteInstrument; final class NoteInstrumentIdMap{ use SingletonTrait; - - /** - * @var NoteInstrument[] - * @phpstan-var array - */ - private array $idToEnum = []; - - /** - * @var int[] - * @phpstan-var array - */ - private array $enumToId = []; + /** @phpstan-use IntSaveIdMapTrait */ + use IntSaveIdMapTrait; private function __construct(){ $this->register(0, NoteInstrument::PIANO()); @@ -59,20 +49,4 @@ final class NoteInstrumentIdMap{ $this->register(14, NoteInstrument::BANJO()); $this->register(15, NoteInstrument::PLING()); } - - private function register(int $id, NoteInstrument $instrument) : void{ - $this->idToEnum[$id] = $instrument; - $this->enumToId[$instrument->id()] = $id; - } - - public function fromId(int $id) : ?NoteInstrument{ - return $this->idToEnum[$id] ?? null; - } - - public function toId(NoteInstrument $instrument) : int{ - if(!isset($this->enumToId[$instrument->id()])){ - throw new \InvalidArgumentException("Type does not have a mapped ID"); - } - return $this->enumToId[$instrument->id()]; - } } diff --git a/src/data/bedrock/PotionTypeIdMap.php b/src/data/bedrock/PotionTypeIdMap.php index 8194f24a2..3e9858217 100644 --- a/src/data/bedrock/PotionTypeIdMap.php +++ b/src/data/bedrock/PotionTypeIdMap.php @@ -28,18 +28,8 @@ use pocketmine\utils\SingletonTrait; final class PotionTypeIdMap{ use SingletonTrait; - - /** - * @var PotionType[] - * @phpstan-var array - */ - private array $idToEnum = []; - - /** - * @var int[] - * @phpstan-var array - */ - private array $enumToId = []; + /** @phpstan-use IntSaveIdMapTrait */ + use IntSaveIdMapTrait; private function __construct(){ $this->register(PotionTypeIds::WATER, PotionType::WATER()); @@ -86,20 +76,4 @@ final class PotionTypeIdMap{ $this->register(PotionTypeIds::LONG_SLOW_FALLING, PotionType::LONG_SLOW_FALLING()); $this->register(PotionTypeIds::STRONG_SLOWNESS, PotionType::STRONG_SLOWNESS()); } - - private function register(int $id, PotionType $type) : void{ - $this->idToEnum[$id] = $type; - $this->enumToId[$type->id()] = $id; - } - - public function fromId(int $id) : ?PotionType{ - return $this->idToEnum[$id] ?? null; - } - - public function toId(PotionType $type) : int{ - if(!isset($this->enumToId[$type->id()])){ - throw new \InvalidArgumentException("Type does not have a mapped ID"); - } - return $this->enumToId[$type->id()]; - } } diff --git a/src/data/bedrock/SuspiciousStewTypeIdMap.php b/src/data/bedrock/SuspiciousStewTypeIdMap.php index 1dc86abf1..37d121517 100644 --- a/src/data/bedrock/SuspiciousStewTypeIdMap.php +++ b/src/data/bedrock/SuspiciousStewTypeIdMap.php @@ -28,18 +28,8 @@ use pocketmine\utils\SingletonTrait; final class SuspiciousStewTypeIdMap{ use SingletonTrait; - - /** - * @var SuspiciousStewType[] - * @phpstan-var array - */ - private array $idToEnum = []; - - /** - * @var int[] - * @phpstan-var array - */ - private array $enumToId = []; + /** @phpstan-use IntSaveIdMapTrait */ + use IntSaveIdMapTrait; private function __construct(){ $this->register(SuspiciousStewTypeIds::POPPY, SuspiciousStewType::POPPY()); @@ -53,20 +43,4 @@ final class SuspiciousStewTypeIdMap{ $this->register(SuspiciousStewTypeIds::OXEYE_DAISY, SuspiciousStewType::OXEYE_DAISY()); $this->register(SuspiciousStewTypeIds::WITHER_ROSE, SuspiciousStewType::WITHER_ROSE()); } - - private function register(int $id, SuspiciousStewType $type) : void{ - $this->idToEnum[$id] = $type; - $this->enumToId[$type->id()] = $id; - } - - public function fromId(int $id) : ?SuspiciousStewType{ - return $this->idToEnum[$id] ?? null; - } - - public function toId(SuspiciousStewType $type) : int{ - if(!isset($this->enumToId[$type->id()])){ - throw new \InvalidArgumentException("Type does not have a mapped ID"); - } - return $this->enumToId[$type->id()]; - } } From d1a7c1d4539e465b6acf5d84fa949c6b6d2a5968 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 25 Aug 2023 12:49:39 +0100 Subject: [PATCH 41/46] Constify server.properties references --- src/Server.php | 88 +++++++++---------- src/ServerProperties.php | 50 +++++++++++ .../defaults/DefaultGamemodeCommand.php | 3 +- src/command/defaults/DifficultyCommand.php | 3 +- src/command/defaults/WhitelistCommand.php | 5 +- src/player/Player.php | 3 +- 6 files changed, 103 insertions(+), 49 deletions(-) create mode 100644 src/ServerProperties.php diff --git a/src/Server.php b/src/Server.php index 148c93b8e..77d2ae393 100644 --- a/src/Server.php +++ b/src/Server.php @@ -357,15 +357,15 @@ class Server{ } public function getPort() : int{ - return $this->configGroup->getConfigInt("server-port", self::DEFAULT_PORT_IPV4); + return $this->configGroup->getConfigInt(ServerProperties::SERVER_PORT_IPV4, self::DEFAULT_PORT_IPV4); } public function getPortV6() : int{ - return $this->configGroup->getConfigInt("server-portv6", self::DEFAULT_PORT_IPV6); + return $this->configGroup->getConfigInt(ServerProperties::SERVER_PORT_IPV6, self::DEFAULT_PORT_IPV6); } public function getViewDistance() : int{ - return max(2, $this->configGroup->getConfigInt("view-distance", self::DEFAULT_MAX_VIEW_DISTANCE)); + return max(2, $this->configGroup->getConfigInt(ServerProperties::VIEW_DISTANCE, self::DEFAULT_MAX_VIEW_DISTANCE)); } /** @@ -376,12 +376,12 @@ class Server{ } public function getIp() : string{ - $str = $this->configGroup->getConfigString("server-ip"); + $str = $this->configGroup->getConfigString(ServerProperties::SERVER_IPV4); return $str !== "" ? $str : "0.0.0.0"; } public function getIpV6() : string{ - $str = $this->configGroup->getConfigString("server-ipv6"); + $str = $this->configGroup->getConfigString(ServerProperties::SERVER_IPV6); return $str !== "" ? $str : "::"; } @@ -390,30 +390,30 @@ class Server{ } public function getGamemode() : GameMode{ - return GameMode::fromString($this->configGroup->getConfigString("gamemode", GameMode::SURVIVAL()->name())) ?? GameMode::SURVIVAL(); + return GameMode::fromString($this->configGroup->getConfigString(ServerProperties::GAME_MODE, GameMode::SURVIVAL()->name())) ?? GameMode::SURVIVAL(); } public function getForceGamemode() : bool{ - return $this->configGroup->getConfigBool("force-gamemode", false); + return $this->configGroup->getConfigBool(ServerProperties::FORCE_GAME_MODE, false); } /** * Returns Server global difficulty. Note that this may be overridden in individual worlds. */ public function getDifficulty() : int{ - return $this->configGroup->getConfigInt("difficulty", World::DIFFICULTY_NORMAL); + return $this->configGroup->getConfigInt(ServerProperties::DIFFICULTY, World::DIFFICULTY_NORMAL); } public function hasWhitelist() : bool{ - return $this->configGroup->getConfigBool("white-list", false); + return $this->configGroup->getConfigBool(ServerProperties::WHITELIST, false); } public function isHardcore() : bool{ - return $this->configGroup->getConfigBool("hardcore", false); + return $this->configGroup->getConfigBool(ServerProperties::HARDCORE, false); } public function getMotd() : string{ - return $this->configGroup->getConfigString("motd", self::DEFAULT_SERVER_NAME); + return $this->configGroup->getConfigString(ServerProperties::MOTD, self::DEFAULT_SERVER_NAME); } public function getLoader() : ThreadSafeClassLoader{ @@ -811,26 +811,26 @@ class Server{ $this->configGroup = new ServerConfigGroup( new Config($pocketmineYmlPath, Config::YAML, []), new Config(Path::join($this->dataPath, "server.properties"), Config::PROPERTIES, [ - "motd" => self::DEFAULT_SERVER_NAME, - "server-port" => self::DEFAULT_PORT_IPV4, - "server-portv6" => self::DEFAULT_PORT_IPV6, - "enable-ipv6" => true, - "white-list" => false, - "max-players" => self::DEFAULT_MAX_PLAYERS, - "gamemode" => GameMode::SURVIVAL()->name(), - "force-gamemode" => false, - "hardcore" => false, - "pvp" => true, - "difficulty" => World::DIFFICULTY_NORMAL, - "generator-settings" => "", - "level-name" => "world", - "level-seed" => "", - "level-type" => "DEFAULT", - "enable-query" => true, - "auto-save" => true, - "view-distance" => self::DEFAULT_MAX_VIEW_DISTANCE, - "xbox-auth" => true, - "language" => "eng" + ServerProperties::MOTD => self::DEFAULT_SERVER_NAME, + ServerProperties::SERVER_PORT_IPV4 => self::DEFAULT_PORT_IPV4, + ServerProperties::SERVER_PORT_IPV6 => self::DEFAULT_PORT_IPV6, + ServerProperties::ENABLE_IPV6 => true, + ServerProperties::WHITELIST => false, + ServerProperties::MAX_PLAYERS => self::DEFAULT_MAX_PLAYERS, + ServerProperties::GAME_MODE => GameMode::SURVIVAL()->name(), + ServerProperties::FORCE_GAME_MODE => false, + ServerProperties::HARDCORE => false, + ServerProperties::PVP => true, + ServerProperties::DIFFICULTY => World::DIFFICULTY_NORMAL, + ServerProperties::DEFAULT_WORLD_GENERATOR_SETTINGS => "", + ServerProperties::DEFAULT_WORLD_NAME => "world", + ServerProperties::DEFAULT_WORLD_SEED => "", + ServerProperties::DEFAULT_WORLD_GENERATOR => "DEFAULT", + ServerProperties::ENABLE_QUERY => true, + ServerProperties::AUTO_SAVE => true, + ServerProperties::VIEW_DISTANCE => self::DEFAULT_MAX_VIEW_DISTANCE, + ServerProperties::XBOX_AUTH => true, + ServerProperties::LANGUAGE => "eng" ]) ); @@ -840,7 +840,7 @@ class Server{ } $this->forceLanguage = $this->configGroup->getPropertyBool("settings.force-language", false); - $selectedLang = $this->configGroup->getConfigString("language", $this->configGroup->getPropertyString("settings.language", Language::FALLBACK_LANGUAGE)); + $selectedLang = $this->configGroup->getConfigString(ServerProperties::LANGUAGE, $this->configGroup->getPropertyString("settings.language", Language::FALLBACK_LANGUAGE)); try{ $this->language = new Language($selectedLang); }catch(LanguageNotFoundException $e){ @@ -932,9 +932,9 @@ class Server{ $this->banByIP = new BanList($bannedIpsTxt); $this->banByIP->load(); - $this->maxPlayers = $this->configGroup->getConfigInt("max-players", self::DEFAULT_MAX_PLAYERS); + $this->maxPlayers = $this->configGroup->getConfigInt(ServerProperties::MAX_PLAYERS, self::DEFAULT_MAX_PLAYERS); - $this->onlineMode = $this->configGroup->getConfigBool("xbox-auth", true); + $this->onlineMode = $this->configGroup->getConfigBool(ServerProperties::XBOX_AUTH, true); if($this->onlineMode){ $this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_auth_enabled())); }else{ @@ -943,8 +943,8 @@ class Server{ $this->logger->warning($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_authProperty_disabled())); } - if($this->configGroup->getConfigBool("hardcore", false) && $this->getDifficulty() < World::DIFFICULTY_HARD){ - $this->configGroup->setConfigInt("difficulty", World::DIFFICULTY_HARD); + if($this->configGroup->getConfigBool(ServerProperties::HARDCORE, false) && $this->getDifficulty() < World::DIFFICULTY_HARD){ + $this->configGroup->setConfigInt(ServerProperties::DIFFICULTY, World::DIFFICULTY_HARD); } @cli_set_process_title($this->getName() . " " . $this->getPocketMineVersion()); @@ -1001,7 +1001,7 @@ class Server{ } $this->worldManager = new WorldManager($this, Path::join($this->dataPath, "worlds"), $providerManager); - $this->worldManager->setAutoSave($this->configGroup->getConfigBool("auto-save", $this->worldManager->getAutoSave())); + $this->worldManager->setAutoSave($this->configGroup->getConfigBool(ServerProperties::AUTO_SAVE, $this->worldManager->getAutoSave())); $this->worldManager->setAutoSaveInterval($this->configGroup->getPropertyInt("ticks-per.autosave", $this->worldManager->getAutoSaveInterval())); $this->updater = new UpdateChecker($this, $this->configGroup->getPropertyString("auto-updater.host", "update.pmmp.io")); @@ -1136,11 +1136,11 @@ class Server{ } if($this->worldManager->getDefaultWorld() === null){ - $default = $this->configGroup->getConfigString("level-name", "world"); + $default = $this->configGroup->getConfigString(ServerProperties::DEFAULT_WORLD_NAME, "world"); if(trim($default) == ""){ $this->getLogger()->warning("level-name cannot be null, using default"); $default = "world"; - $this->configGroup->setConfigString("level-name", "world"); + $this->configGroup->setConfigString(ServerProperties::DEFAULT_WORLD_NAME, "world"); } if(!$this->worldManager->loadWorld($default, true)){ if($this->worldManager->isWorldGenerated($default)){ @@ -1148,8 +1148,8 @@ class Server{ return false; } - $generatorName = $this->configGroup->getConfigString("level-type"); - $generatorOptions = $this->configGroup->getConfigString("generator-settings"); + $generatorName = $this->configGroup->getConfigString(ServerProperties::DEFAULT_WORLD_GENERATOR); + $generatorOptions = $this->configGroup->getConfigString(ServerProperties::DEFAULT_WORLD_GENERATOR_SETTINGS); $generatorClass = $getGenerator($generatorName, $generatorOptions, $default); if($generatorClass === null){ @@ -1159,7 +1159,7 @@ class Server{ $creationOptions = WorldCreationOptions::create() ->setGeneratorClass($generatorClass) ->setGeneratorOptions($generatorOptions); - $convertedSeed = Generator::convertSeed($this->configGroup->getConfigString("level-seed")); + $convertedSeed = Generator::convertSeed($this->configGroup->getConfigString(ServerProperties::DEFAULT_WORLD_SEED)); if($convertedSeed !== null){ $creationOptions->setSeed($convertedSeed); } @@ -1213,7 +1213,7 @@ class Server{ } private function startupPrepareNetworkInterfaces() : bool{ - $useQuery = $this->configGroup->getConfigBool("enable-query", true); + $useQuery = $this->configGroup->getConfigBool(ServerProperties::ENABLE_QUERY, true); $typeConverter = TypeConverter::getInstance(); $packetSerializerContext = new PacketSerializerContext($typeConverter->getItemTypeDictionary()); @@ -1223,7 +1223,7 @@ class Server{ if( !$this->startupPrepareConnectableNetworkInterfaces($this->getIp(), $this->getPort(), false, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext, $typeConverter) || ( - $this->configGroup->getConfigBool("enable-ipv6", true) && + $this->configGroup->getConfigBool(ServerProperties::ENABLE_IPV6, true) && !$this->startupPrepareConnectableNetworkInterfaces($this->getIpV6(), $this->getPortV6(), true, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext, $typeConverter) ) ){ diff --git a/src/ServerProperties.php b/src/ServerProperties.php new file mode 100644 index 000000000..884ff4f67 --- /dev/null +++ b/src/ServerProperties.php @@ -0,0 +1,50 @@ +getServer()->getConfigGroup()->setConfigString("gamemode", $gameMode->name()); + $sender->getServer()->getConfigGroup()->setConfigString(ServerProperties::GAME_MODE, $gameMode->name()); $sender->sendMessage(KnownTranslationFactory::commands_defaultgamemode_success($gameMode->getTranslatableName())); return true; } diff --git a/src/command/defaults/DifficultyCommand.php b/src/command/defaults/DifficultyCommand.php index 98eb3be3b..dee75e025 100644 --- a/src/command/defaults/DifficultyCommand.php +++ b/src/command/defaults/DifficultyCommand.php @@ -28,6 +28,7 @@ use pocketmine\command\CommandSender; use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\lang\KnownTranslationFactory; use pocketmine\permission\DefaultPermissionNames; +use pocketmine\ServerProperties; use pocketmine\world\World; use function count; @@ -54,7 +55,7 @@ class DifficultyCommand extends VanillaCommand{ } if($difficulty !== -1){ - $sender->getServer()->getConfigGroup()->setConfigInt("difficulty", $difficulty); + $sender->getServer()->getConfigGroup()->setConfigInt(ServerProperties::DIFFICULTY, $difficulty); //TODO: add per-world support foreach($sender->getServer()->getWorldManager()->getWorlds() as $world){ diff --git a/src/command/defaults/WhitelistCommand.php b/src/command/defaults/WhitelistCommand.php index 65860aefa..fdf01ff56 100644 --- a/src/command/defaults/WhitelistCommand.php +++ b/src/command/defaults/WhitelistCommand.php @@ -30,6 +30,7 @@ use pocketmine\lang\KnownTranslationFactory; use pocketmine\permission\DefaultPermissionNames; use pocketmine\player\Player; use pocketmine\Server; +use pocketmine\ServerProperties; use function count; use function implode; use function sort; @@ -71,7 +72,7 @@ class WhitelistCommand extends VanillaCommand{ case "on": if($this->testPermission($sender, DefaultPermissionNames::COMMAND_WHITELIST_ENABLE)){ $server = $sender->getServer(); - $server->getConfigGroup()->setConfigBool("white-list", true); + $server->getConfigGroup()->setConfigBool(ServerProperties::WHITELIST, true); $this->kickNonWhitelistedPlayers($server); Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_whitelist_enabled()); } @@ -79,7 +80,7 @@ class WhitelistCommand extends VanillaCommand{ return true; case "off": if($this->testPermission($sender, DefaultPermissionNames::COMMAND_WHITELIST_DISABLE)){ - $sender->getServer()->getConfigGroup()->setConfigBool("white-list", false); + $sender->getServer()->getConfigGroup()->setConfigBool(ServerProperties::WHITELIST, false); Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_whitelist_disabled()); } diff --git a/src/player/Player.php b/src/player/Player.php index d34a357ee..33f9e16f4 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -119,6 +119,7 @@ use pocketmine\permission\PermissibleBase; use pocketmine\permission\PermissibleDelegateTrait; use pocketmine\player\chat\StandardChatFormatter; use pocketmine\Server; +use pocketmine\ServerProperties; use pocketmine\timings\Timings; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\TextFormat; @@ -1844,7 +1845,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ if(!$this->canInteract($entity->getLocation(), self::MAX_REACH_DISTANCE_ENTITY_INTERACTION)){ $this->logger->debug("Cancelled attack of entity " . $entity->getId() . " due to not currently being interactable"); $ev->cancel(); - }elseif($this->isSpectator() || ($entity instanceof Player && !$this->server->getConfigGroup()->getConfigBool("pvp"))){ + }elseif($this->isSpectator() || ($entity instanceof Player && !$this->server->getConfigGroup()->getConfigBool(ServerProperties::PVP))){ $ev->cancel(); } From 506d8d10642e1c6af64b39788cba129f406ee499 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 25 Aug 2023 12:49:56 +0100 Subject: [PATCH 42/46] CS --- src/data/bedrock/EffectIdMap.php | 2 -- src/data/bedrock/EnchantmentIdMap.php | 2 -- src/data/bedrock/IntSaveIdMapTrait.php | 1 - src/data/bedrock/MushroomBlockTypeIdMap.php | 1 - 4 files changed, 6 deletions(-) diff --git a/src/data/bedrock/EffectIdMap.php b/src/data/bedrock/EffectIdMap.php index 6efa10b41..cadd8c397 100644 --- a/src/data/bedrock/EffectIdMap.php +++ b/src/data/bedrock/EffectIdMap.php @@ -26,8 +26,6 @@ namespace pocketmine\data\bedrock; use pocketmine\entity\effect\Effect; use pocketmine\entity\effect\VanillaEffects; use pocketmine\utils\SingletonTrait; -use function array_key_exists; -use function spl_object_id; final class EffectIdMap{ use SingletonTrait; diff --git a/src/data/bedrock/EnchantmentIdMap.php b/src/data/bedrock/EnchantmentIdMap.php index c06080e3e..ae2460cfe 100644 --- a/src/data/bedrock/EnchantmentIdMap.php +++ b/src/data/bedrock/EnchantmentIdMap.php @@ -26,8 +26,6 @@ namespace pocketmine\data\bedrock; use pocketmine\item\enchantment\Enchantment; use pocketmine\item\enchantment\VanillaEnchantments; use pocketmine\utils\SingletonTrait; -use function array_key_exists; -use function spl_object_id; /** * Handles translation of internal enchantment types to and from Minecraft: Bedrock IDs. diff --git a/src/data/bedrock/IntSaveIdMapTrait.php b/src/data/bedrock/IntSaveIdMapTrait.php index 041dfa9e6..cf1631fd5 100644 --- a/src/data/bedrock/IntSaveIdMapTrait.php +++ b/src/data/bedrock/IntSaveIdMapTrait.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace pocketmine\data\bedrock; -use function abs; use function array_key_exists; use function spl_object_id; diff --git a/src/data/bedrock/MushroomBlockTypeIdMap.php b/src/data/bedrock/MushroomBlockTypeIdMap.php index 5e195ebb2..927b52c76 100644 --- a/src/data/bedrock/MushroomBlockTypeIdMap.php +++ b/src/data/bedrock/MushroomBlockTypeIdMap.php @@ -26,7 +26,6 @@ namespace pocketmine\data\bedrock; use pocketmine\block\utils\MushroomBlockType; use pocketmine\data\bedrock\block\BlockLegacyMetadata as LegacyMeta; use pocketmine\utils\SingletonTrait; -use function array_key_exists; final class MushroomBlockTypeIdMap{ use SingletonTrait; From 31d8cc1cb556c9a95960e56de0fffbef13833b72 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 25 Aug 2023 13:23:38 +0100 Subject: [PATCH 43/46] Generate and use constants for pocketmine.yml constant names a couple of usages of properties that no longer exist couldn't be migrated. in addition, this revealed a couple of dead properties in the default file. this is not an ideal solution (I'd much rather model the configs using classes and map them) but in the absence of a good and reliable library to do that, this is the next best thing. --- .github/workflows/main.yml | 3 + ...enerate-pocketmine-yml-property-consts.php | 76 +++++++++++++ src/MemoryManager.php | 29 ++--- src/Server.php | 59 +++++----- src/YmlServerProperties.php | 107 ++++++++++++++++++ src/command/defaults/TimingsCommand.php | 3 +- src/crash/CrashDump.php | 7 +- src/network/mcpe/NetworkSession.php | 3 +- src/network/mcpe/raklib/RakLibInterface.php | 3 +- src/network/query/QueryInfo.php | 3 +- src/player/Player.php | 7 +- src/stats/SendUsageTask.php | 3 +- src/updater/UpdateChecker.php | 7 +- src/world/World.php | 9 +- 14 files changed, 258 insertions(+), 61 deletions(-) create mode 100644 build/generate-pocketmine-yml-property-consts.php create mode 100644 src/YmlServerProperties.php diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 022e0ab3a..a888d4254 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -158,6 +158,9 @@ jobs: - name: Regenerate BedrockData available files constants run: php build/generate-bedrockdata-path-consts.php + - name: Regenerate YmlServerProperties constants + run: php build/generate-pocketmine-yml-property-consts.php + - name: Verify code is unchanged run: | git diff diff --git a/build/generate-pocketmine-yml-property-consts.php b/build/generate-pocketmine-yml-property-consts.php new file mode 100644 index 000000000..b0e79519a --- /dev/null +++ b/build/generate-pocketmine-yml-property-consts.php @@ -0,0 +1,76 @@ + $constants + * @phpstan-param-out array $constants + */ +function collectProperties(string $prefix, array $properties, array &$constants) : void{ + foreach($properties as $propertyName => $property){ + $fullPropertyName = ($prefix !== "" ? $prefix . "." : "") . $propertyName; + + $constName = str_replace([".", "-"], "_", strtoupper($fullPropertyName)); + $constants[$constName] = $fullPropertyName; + + if(is_array($property)){ + collectProperties($fullPropertyName, $property, $constants); + } + } +} + +collectProperties("", $defaultConfig, $constants); +ksort($constants, SORT_STRING); + +$file = fopen(dirname(__DIR__) . '/src/YmlServerProperties.php', 'wb'); +if($file === false){ + fwrite(STDERR, "Failed to open output file\n"); + exit(1); +} +fwrite($file, " $propertyName){ + fwrite($file, "\tpublic const $constName = '$propertyName';\n"); +} +fwrite($file, "}\n"); + +fclose($file); + +echo "Done. Don't forget to run CS fixup after generating code.\n"; diff --git a/src/MemoryManager.php b/src/MemoryManager.php index 0e8502ff8..053498e7e 100644 --- a/src/MemoryManager.php +++ b/src/MemoryManager.php @@ -30,6 +30,7 @@ use pocketmine\scheduler\GarbageCollectionTask; use pocketmine\timings\Timings; use pocketmine\utils\Process; use pocketmine\utils\Utils; +use pocketmine\YmlServerProperties as Yml; use Symfony\Component\Filesystem\Path; use function arsort; use function count; @@ -109,7 +110,7 @@ class MemoryManager{ } private function init(ServerConfigGroup $config) : void{ - $this->memoryLimit = $config->getPropertyInt("memory.main-limit", 0) * 1024 * 1024; + $this->memoryLimit = $config->getPropertyInt(Yml::MEMORY_MAIN_LIMIT, 0) * 1024 * 1024; $defaultMemory = 1024; @@ -127,7 +128,7 @@ class MemoryManager{ } } - $hardLimit = $config->getPropertyInt("memory.main-hard-limit", $defaultMemory); + $hardLimit = $config->getPropertyInt(Yml::MEMORY_MAIN_HARD_LIMIT, $defaultMemory); if($hardLimit <= 0){ ini_set("memory_limit", '-1'); @@ -135,22 +136,22 @@ class MemoryManager{ ini_set("memory_limit", $hardLimit . "M"); } - $this->globalMemoryLimit = $config->getPropertyInt("memory.global-limit", 0) * 1024 * 1024; - $this->checkRate = $config->getPropertyInt("memory.check-rate", self::DEFAULT_CHECK_RATE); - $this->continuousTrigger = $config->getPropertyBool("memory.continuous-trigger", true); - $this->continuousTriggerRate = $config->getPropertyInt("memory.continuous-trigger-rate", self::DEFAULT_CONTINUOUS_TRIGGER_RATE); + $this->globalMemoryLimit = $config->getPropertyInt(Yml::MEMORY_GLOBAL_LIMIT, 0) * 1024 * 1024; + $this->checkRate = $config->getPropertyInt(Yml::MEMORY_CHECK_RATE, self::DEFAULT_CHECK_RATE); + $this->continuousTrigger = $config->getPropertyBool(Yml::MEMORY_CONTINUOUS_TRIGGER, true); + $this->continuousTriggerRate = $config->getPropertyInt(Yml::MEMORY_CONTINUOUS_TRIGGER_RATE, self::DEFAULT_CONTINUOUS_TRIGGER_RATE); - $this->garbageCollectionPeriod = $config->getPropertyInt("memory.garbage-collection.period", self::DEFAULT_TICKS_PER_GC); - $this->garbageCollectionTrigger = $config->getPropertyBool("memory.garbage-collection.low-memory-trigger", true); - $this->garbageCollectionAsync = $config->getPropertyBool("memory.garbage-collection.collect-async-worker", true); + $this->garbageCollectionPeriod = $config->getPropertyInt(Yml::MEMORY_GARBAGE_COLLECTION_PERIOD, self::DEFAULT_TICKS_PER_GC); + $this->garbageCollectionTrigger = $config->getPropertyBool(Yml::MEMORY_GARBAGE_COLLECTION_LOW_MEMORY_TRIGGER, true); + $this->garbageCollectionAsync = $config->getPropertyBool(Yml::MEMORY_GARBAGE_COLLECTION_COLLECT_ASYNC_WORKER, true); - $this->lowMemChunkRadiusOverride = $config->getPropertyInt("memory.max-chunks.chunk-radius", 4); - $this->lowMemChunkGC = $config->getPropertyBool("memory.max-chunks.trigger-chunk-collect", true); + $this->lowMemChunkRadiusOverride = $config->getPropertyInt(Yml::MEMORY_MAX_CHUNKS_CHUNK_RADIUS, 4); + $this->lowMemChunkGC = $config->getPropertyBool(Yml::MEMORY_MAX_CHUNKS_TRIGGER_CHUNK_COLLECT, true); - $this->lowMemDisableChunkCache = $config->getPropertyBool("memory.world-caches.disable-chunk-cache", true); - $this->lowMemClearWorldCache = $config->getPropertyBool("memory.world-caches.low-memory-trigger", true); + $this->lowMemDisableChunkCache = $config->getPropertyBool(Yml::MEMORY_WORLD_CACHES_DISABLE_CHUNK_CACHE, true); + $this->lowMemClearWorldCache = $config->getPropertyBool(Yml::MEMORY_WORLD_CACHES_LOW_MEMORY_TRIGGER, true); - $this->dumpWorkers = $config->getPropertyBool("memory.memory-dump.dump-async-worker", true); + $this->dumpWorkers = $config->getPropertyBool(Yml::MEMORY_MEMORY_DUMP_DUMP_ASYNC_WORKER, true); gc_enable(); } diff --git a/src/Server.php b/src/Server.php index 77d2ae393..00951c10a 100644 --- a/src/Server.php +++ b/src/Server.php @@ -120,6 +120,7 @@ use pocketmine\world\Position; use pocketmine\world\World; use pocketmine\world\WorldCreationOptions; use pocketmine\world\WorldManager; +use pocketmine\YmlServerProperties as Yml; use Ramsey\Uuid\UuidInterface; use Symfony\Component\Filesystem\Path; use function array_fill; @@ -496,7 +497,7 @@ class Server{ } public function shouldSavePlayerData() : bool{ - return $this->configGroup->getPropertyBool("player.save-player-data", true); + return $this->configGroup->getPropertyBool(Yml::PLAYER_SAVE_PLAYER_DATA, true); } public function getOfflinePlayer(string $name) : Player|OfflinePlayer|null{ @@ -736,7 +737,7 @@ class Server{ * @return string[][] */ public function getCommandAliases() : array{ - $section = $this->configGroup->getProperty("aliases"); + $section = $this->configGroup->getProperty(YmlServerProperties::ALIASES); $result = []; if(is_array($section)){ foreach($section as $key => $value){ @@ -834,12 +835,12 @@ class Server{ ]) ); - $debugLogLevel = $this->configGroup->getPropertyInt("debug.level", 1); + $debugLogLevel = $this->configGroup->getPropertyInt(Yml::DEBUG_LEVEL, 1); if($this->logger instanceof MainLogger){ $this->logger->setLogDebug($debugLogLevel > 1); } - $this->forceLanguage = $this->configGroup->getPropertyBool("settings.force-language", false); + $this->forceLanguage = $this->configGroup->getPropertyBool(Yml::SETTINGS_FORCE_LANGUAGE, false); $selectedLang = $this->configGroup->getConfigString(ServerProperties::LANGUAGE, $this->configGroup->getPropertyString("settings.language", Language::FALLBACK_LANGUAGE)); try{ $this->language = new Language($selectedLang); @@ -856,11 +857,11 @@ class Server{ $this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::language_selected($this->getLanguage()->getName(), $this->getLanguage()->getLang()))); if(VersionInfo::IS_DEVELOPMENT_BUILD){ - if(!$this->configGroup->getPropertyBool("settings.enable-dev-builds", false)){ + if(!$this->configGroup->getPropertyBool(Yml::SETTINGS_ENABLE_DEV_BUILDS, false)){ $this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error1(VersionInfo::NAME))); $this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error2())); $this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error3())); - $this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error4("settings.enable-dev-builds"))); + $this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error4(YmlServerProperties::SETTINGS_ENABLE_DEV_BUILDS))); $this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_devBuild_error5("https://github.com/pmmp/PocketMine-MP/releases"))); $this->forceShutdownExit(); @@ -878,7 +879,7 @@ class Server{ $this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_start(TextFormat::AQUA . $this->getVersion() . TextFormat::RESET))); - if(($poolSize = $this->configGroup->getPropertyString("settings.async-workers", "auto")) === "auto"){ + if(($poolSize = $this->configGroup->getPropertyString(Yml::SETTINGS_ASYNC_WORKERS, "auto")) === "auto"){ $poolSize = 2; $processors = Utils::getCoreCount() - 2; @@ -889,32 +890,32 @@ class Server{ $poolSize = max(1, (int) $poolSize); } - $this->asyncPool = new AsyncPool($poolSize, max(-1, $this->configGroup->getPropertyInt("memory.async-worker-hard-limit", 256)), $this->autoloader, $this->logger, $this->tickSleeper); + $this->asyncPool = new AsyncPool($poolSize, max(-1, $this->configGroup->getPropertyInt(Yml::MEMORY_ASYNC_WORKER_HARD_LIMIT, 256)), $this->autoloader, $this->logger, $this->tickSleeper); $netCompressionThreshold = -1; - if($this->configGroup->getPropertyInt("network.batch-threshold", 256) >= 0){ - $netCompressionThreshold = $this->configGroup->getPropertyInt("network.batch-threshold", 256); + if($this->configGroup->getPropertyInt(Yml::NETWORK_BATCH_THRESHOLD, 256) >= 0){ + $netCompressionThreshold = $this->configGroup->getPropertyInt(Yml::NETWORK_BATCH_THRESHOLD, 256); } if($netCompressionThreshold < 0){ $netCompressionThreshold = null; } - $netCompressionLevel = $this->configGroup->getPropertyInt("network.compression-level", 6); + $netCompressionLevel = $this->configGroup->getPropertyInt(Yml::NETWORK_COMPRESSION_LEVEL, 6); if($netCompressionLevel < 1 || $netCompressionLevel > 9){ $this->logger->warning("Invalid network compression level $netCompressionLevel set, setting to default 6"); $netCompressionLevel = 6; } ZlibCompressor::setInstance(new ZlibCompressor($netCompressionLevel, $netCompressionThreshold, ZlibCompressor::DEFAULT_MAX_DECOMPRESSION_SIZE)); - $this->networkCompressionAsync = $this->configGroup->getPropertyBool("network.async-compression", true); + $this->networkCompressionAsync = $this->configGroup->getPropertyBool(Yml::NETWORK_ASYNC_COMPRESSION, true); $this->networkCompressionAsyncThreshold = max( - $this->configGroup->getPropertyInt("network.async-compression-threshold", self::DEFAULT_ASYNC_COMPRESSION_THRESHOLD), + $this->configGroup->getPropertyInt(Yml::NETWORK_ASYNC_COMPRESSION_THRESHOLD, self::DEFAULT_ASYNC_COMPRESSION_THRESHOLD), $netCompressionThreshold ?? self::DEFAULT_ASYNC_COMPRESSION_THRESHOLD ); - EncryptionContext::$ENABLED = $this->configGroup->getPropertyBool("network.enable-encryption", true); + EncryptionContext::$ENABLED = $this->configGroup->getPropertyBool(Yml::NETWORK_ENABLE_ENCRYPTION, true); - $this->doTitleTick = $this->configGroup->getPropertyBool("console.title-tick", true) && Terminal::hasFormattingCodes(); + $this->doTitleTick = $this->configGroup->getPropertyBool(Yml::CONSOLE_TITLE_TICK, true) && Terminal::hasFormattingCodes(); $this->operators = new Config(Path::join($this->dataPath, "ops.txt"), Config::ENUM); $this->whitelist = new Config(Path::join($this->dataPath, "white-list.txt"), Config::ENUM); @@ -963,8 +964,8 @@ class Server{ ))); $this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_license($this->getName()))); - TimingsHandler::setEnabled($this->configGroup->getPropertyBool("settings.enable-profiling", false)); - $this->profilingTickRate = $this->configGroup->getPropertyInt("settings.profile-report-trigger", self::TARGET_TICKS_PER_SECOND); + TimingsHandler::setEnabled($this->configGroup->getPropertyBool(Yml::SETTINGS_ENABLE_PROFILING, false)); + $this->profilingTickRate = $this->configGroup->getPropertyInt(Yml::SETTINGS_PROFILE_REPORT_TRIGGER, self::TARGET_TICKS_PER_SECOND); DefaultPermissions::registerCorePermissions(); @@ -986,13 +987,13 @@ class Server{ $this->forceShutdownExit(); return; } - $this->pluginManager = new PluginManager($this, $this->configGroup->getPropertyBool("plugins.legacy-data-dir", true) ? null : Path::join($this->getDataPath(), "plugin_data"), $pluginGraylist); + $this->pluginManager = new PluginManager($this, $this->configGroup->getPropertyBool(Yml::PLUGINS_LEGACY_DATA_DIR, true) ? null : Path::join($this->getDataPath(), "plugin_data"), $pluginGraylist); $this->pluginManager->registerInterface(new PharPluginLoader($this->autoloader)); $this->pluginManager->registerInterface(new ScriptPluginLoader()); $providerManager = new WorldProviderManager(); if( - ($format = $providerManager->getProviderByName($formatName = $this->configGroup->getPropertyString("level-settings.default-format", ""))) !== null && + ($format = $providerManager->getProviderByName($formatName = $this->configGroup->getPropertyString(Yml::LEVEL_SETTINGS_DEFAULT_FORMAT, ""))) !== null && $format instanceof WritableWorldProviderManagerEntry ){ $providerManager->setDefault($format); @@ -1002,9 +1003,9 @@ class Server{ $this->worldManager = new WorldManager($this, Path::join($this->dataPath, "worlds"), $providerManager); $this->worldManager->setAutoSave($this->configGroup->getConfigBool(ServerProperties::AUTO_SAVE, $this->worldManager->getAutoSave())); - $this->worldManager->setAutoSaveInterval($this->configGroup->getPropertyInt("ticks-per.autosave", $this->worldManager->getAutoSaveInterval())); + $this->worldManager->setAutoSaveInterval($this->configGroup->getPropertyInt(Yml::TICKS_PER_AUTOSAVE, $this->worldManager->getAutoSaveInterval())); - $this->updater = new UpdateChecker($this, $this->configGroup->getPropertyString("auto-updater.host", "update.pmmp.io")); + $this->updater = new UpdateChecker($this, $this->configGroup->getPropertyString(Yml::AUTO_UPDATER_HOST, "update.pmmp.io")); $this->queryInfo = new QueryInfo($this); @@ -1041,7 +1042,7 @@ class Server{ return; } - if($this->configGroup->getPropertyBool("anonymous-statistics.enabled", true)){ + if($this->configGroup->getPropertyBool(Yml::ANONYMOUS_STATISTICS_ENABLED, true)){ $this->sendUsageTicker = self::TICKS_PER_STATS_REPORT; $this->sendUsage(SendUsageTask::TYPE_OPEN); } @@ -1057,7 +1058,7 @@ class Server{ $this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_USERS, $forwarder); //TODO: move console parts to a separate component - if($this->configGroup->getPropertyBool("console.enable-input", true)){ + if($this->configGroup->getPropertyBool(Yml::CONSOLE_ENABLE_INPUT, true)){ $this->console = new ConsoleReaderChildProcessDaemon($this->logger); } @@ -1092,7 +1093,7 @@ class Server{ $anyWorldFailedToLoad = false; - foreach((array) $this->configGroup->getProperty("worlds", []) as $name => $options){ + foreach((array) $this->configGroup->getProperty(Yml::WORLDS, []) as $name => $options){ if($options === null){ $options = []; }elseif(!is_array($options)){ @@ -1238,7 +1239,7 @@ class Server{ $this->network->blockAddress($entry->getName(), -1); } - if($this->configGroup->getPropertyBool("network.upnp-forwarding", false)){ + if($this->configGroup->getPropertyBool(Yml::NETWORK_UPNP_FORWARDING, false)){ $this->network->registerInterface(new UPnPNetworkInterface($this->logger, Internet::getInternalIP(), $this->getPort())); } @@ -1458,7 +1459,7 @@ class Server{ } if(isset($this->network)){ - $this->network->getSessionManager()->close($this->configGroup->getPropertyString("settings.shutdown-message", "Server closed")); + $this->network->getSessionManager()->close($this->configGroup->getPropertyString(YmlServerProperties::SETTINGS_SHUTDOWN_MESSAGE, "Server closed")); } if(isset($this->worldManager)){ @@ -1595,7 +1596,7 @@ class Server{ $this->logger->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_crash_submit($crashDumpPath))); - if($this->configGroup->getPropertyBool("auto-report.enabled", true)){ + if($this->configGroup->getPropertyBool(Yml::AUTO_REPORT_ENABLED, true)){ $report = true; $stamp = Path::join($this->getDataPath(), "crashdumps", ".last_crash"); @@ -1625,7 +1626,7 @@ class Server{ } if($report){ - $url = ($this->configGroup->getPropertyBool("auto-report.use-https", true) ? "https" : "http") . "://" . $this->configGroup->getPropertyString("auto-report.host", "crash.pmmp.io") . "/submit/api"; + $url = ($this->configGroup->getPropertyBool(Yml::AUTO_REPORT_USE_HTTPS, true) ? "https" : "http") . "://" . $this->configGroup->getPropertyString(Yml::AUTO_REPORT_HOST, "crash.pmmp.io") . "/submit/api"; $postUrlError = "Unknown error"; $reply = Internet::postURL($url, [ "report" => "yes", @@ -1736,7 +1737,7 @@ class Server{ } public function sendUsage(int $type = SendUsageTask::TYPE_STATUS) : void{ - if($this->configGroup->getPropertyBool("anonymous-statistics.enabled", true)){ + if($this->configGroup->getPropertyBool(Yml::ANONYMOUS_STATISTICS_ENABLED, true)){ $this->asyncPool->submitTask(new SendUsageTask($this, $type, $this->uniquePlayers)); } $this->uniquePlayers = []; diff --git a/src/YmlServerProperties.php b/src/YmlServerProperties.php new file mode 100644 index 000000000..920f0797a --- /dev/null +++ b/src/YmlServerProperties.php @@ -0,0 +1,107 @@ +getServer()->getConfigGroup()->getPropertyString("timings.host", "timings.pmmp.io"); + $host = $sender->getServer()->getConfigGroup()->getPropertyString(YmlServerProperties::TIMINGS_HOST, "timings.pmmp.io"); $sender->getServer()->getAsyncPool()->submitTask(new BulkCurlTask( [new BulkCurlTaskOperation( diff --git a/src/crash/CrashDump.php b/src/crash/CrashDump.php index ded1e5d88..9d2eb39e9 100644 --- a/src/crash/CrashDump.php +++ b/src/crash/CrashDump.php @@ -34,6 +34,7 @@ use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Filesystem; use pocketmine\utils\Utils; use pocketmine\VersionInfo; +use pocketmine\YmlServerProperties; use Symfony\Component\Filesystem\Path; use function array_map; use function base64_encode; @@ -152,7 +153,7 @@ class CrashDump{ private function extraData() : void{ global $argv; - if($this->server->getConfigGroup()->getPropertyBool("auto-report.send-settings", true)){ + if($this->server->getConfigGroup()->getPropertyBool(YmlServerProperties::AUTO_REPORT_SEND_SETTINGS, true)){ $this->data->parameters = (array) $argv; if(($serverDotProperties = @file_get_contents(Path::join($this->server->getDataPath(), "server.properties"))) !== false){ $this->data->serverDotProperties = preg_replace("#^rcon\\.password=(.*)$#m", "rcon.password=******", $serverDotProperties) ?? throw new AssumptionFailedError("Pattern is valid"); @@ -170,7 +171,7 @@ class CrashDump{ $this->data->jit_mode = Utils::getOpcacheJitMode(); - if($this->server->getConfigGroup()->getPropertyBool("auto-report.send-phpinfo", true)){ + if($this->server->getConfigGroup()->getPropertyBool(YmlServerProperties::AUTO_REPORT_SEND_PHPINFO, true)){ ob_start(); phpinfo(); $this->data->phpinfo = ob_get_contents(); // @phpstan-ignore-line @@ -225,7 +226,7 @@ class CrashDump{ } } - if($this->server->getConfigGroup()->getPropertyBool("auto-report.send-code", true) && file_exists($error["fullFile"])){ + if($this->server->getConfigGroup()->getPropertyBool(YmlServerProperties::AUTO_REPORT_SEND_CODE, true) && file_exists($error["fullFile"])){ $file = @file($error["fullFile"], FILE_IGNORE_NEW_LINES); if($file !== false){ for($l = max(0, $error["line"] - 10); $l < $error["line"] + 10 && isset($file[$l]); ++$l){ diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 100d9c989..5b65ca1b5 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -108,6 +108,7 @@ use pocketmine\utils\BinaryStream; use pocketmine\utils\ObjectSet; use pocketmine\utils\TextFormat; use pocketmine\world\Position; +use pocketmine\YmlServerProperties; use function array_map; use function array_values; use function base64_encode; @@ -740,7 +741,7 @@ class NetworkSession{ } $this->logger->debug("Xbox Live authenticated: " . ($this->authenticated ? "YES" : "NO")); - $checkXUID = $this->server->getConfigGroup()->getPropertyBool("player.verify-xuid", true); + $checkXUID = $this->server->getConfigGroup()->getPropertyBool(YmlServerProperties::PLAYER_VERIFY_XUID, true); $myXUID = $this->info instanceof XboxLivePlayerInfo ? $this->info->getXuid() : ""; $kickForXUIDMismatch = function(string $xuid) use ($checkXUID, $myXUID) : bool{ if($checkXUID && $myXUID !== $xuid){ diff --git a/src/network/mcpe/raklib/RakLibInterface.php b/src/network/mcpe/raklib/RakLibInterface.php index 4bf8ffb15..759a992e8 100644 --- a/src/network/mcpe/raklib/RakLibInterface.php +++ b/src/network/mcpe/raklib/RakLibInterface.php @@ -42,6 +42,7 @@ use pocketmine\Server; use pocketmine\thread\ThreadCrashException; use pocketmine\timings\Timings; use pocketmine\utils\Utils; +use pocketmine\YmlServerProperties; use raklib\generic\DisconnectReason; use raklib\generic\SocketException; use raklib\protocol\EncapsulatedPacket; @@ -125,7 +126,7 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{ $threadToMainBuffer, new InternetAddress($ip, $port, $ipV6 ? 6 : 4), $this->rakServerId, - $this->server->getConfigGroup()->getPropertyInt("network.max-mtu-size", 1492), + $this->server->getConfigGroup()->getPropertyInt(YmlServerProperties::NETWORK_MAX_MTU_SIZE, 1492), self::MCPE_RAKNET_PROTOCOL_VERSION, $sleeperEntry ); diff --git a/src/network/query/QueryInfo.php b/src/network/query/QueryInfo.php index 2d36ac7e6..cdb1c66db 100644 --- a/src/network/query/QueryInfo.php +++ b/src/network/query/QueryInfo.php @@ -29,6 +29,7 @@ use pocketmine\plugin\Plugin; use pocketmine\Server; use pocketmine\utils\Binary; use pocketmine\utils\Utils; +use pocketmine\YmlServerProperties; use function array_map; use function chr; use function count; @@ -66,7 +67,7 @@ final class QueryInfo{ public function __construct(Server $server){ $this->serverName = $server->getMotd(); - $this->listPlugins = $server->getConfigGroup()->getPropertyBool("settings.query-plugins", true); + $this->listPlugins = $server->getConfigGroup()->getPropertyBool(YmlServerProperties::SETTINGS_QUERY_PLUGINS, true); $this->plugins = $server->getPluginManager()->getPlugins(); $this->players = array_map(fn(Player $p) => $p->getName(), $server->getOnlinePlayers()); diff --git a/src/player/Player.php b/src/player/Player.php index 33f9e16f4..2059613ad 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -135,6 +135,7 @@ use pocketmine\world\sound\FireExtinguishSound; use pocketmine\world\sound\ItemBreakSound; use pocketmine\world\sound\Sound; use pocketmine\world\World; +use pocketmine\YmlServerProperties; use Ramsey\Uuid\UuidInterface; use function abs; use function array_filter; @@ -318,8 +319,8 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $rootPermissions[DefaultPermissions::ROOT_OPERATOR] = true; } $this->perm = new PermissibleBase($rootPermissions); - $this->chunksPerTick = $this->server->getConfigGroup()->getPropertyInt("chunk-sending.per-tick", 4); - $this->spawnThreshold = (int) (($this->server->getConfigGroup()->getPropertyInt("chunk-sending.spawn-radius", 4) ** 2) * M_PI); + $this->chunksPerTick = $this->server->getConfigGroup()->getPropertyInt(YmlServerProperties::CHUNK_SENDING_PER_TICK, 4); + $this->spawnThreshold = (int) (($this->server->getConfigGroup()->getPropertyInt(YmlServerProperties::CHUNK_SENDING_SPAWN_RADIUS, 4) ** 2) * M_PI); $this->chunkSelector = new ChunkSelector(); $this->chunkLoader = new class implements ChunkLoader{}; @@ -584,7 +585,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $this->viewDistance = $newViewDistance; - $this->spawnThreshold = (int) (min($this->viewDistance, $this->server->getConfigGroup()->getPropertyInt("chunk-sending.spawn-radius", 4)) ** 2 * M_PI); + $this->spawnThreshold = (int) (min($this->viewDistance, $this->server->getConfigGroup()->getPropertyInt(YmlServerProperties::CHUNK_SENDING_SPAWN_RADIUS, 4)) ** 2 * M_PI); $this->nextChunkOrderRun = 0; diff --git a/src/stats/SendUsageTask.php b/src/stats/SendUsageTask.php index 08c17c73a..d218774b5 100644 --- a/src/stats/SendUsageTask.php +++ b/src/stats/SendUsageTask.php @@ -31,6 +31,7 @@ use pocketmine\utils\Internet; use pocketmine\utils\Process; use pocketmine\utils\Utils; use pocketmine\VersionInfo; +use pocketmine\YmlServerProperties; use Ramsey\Uuid\Uuid; use function array_map; use function array_values; @@ -57,7 +58,7 @@ class SendUsageTask extends AsyncTask{ * @phpstan-param array $playerList */ public function __construct(Server $server, int $type, array $playerList = []){ - $endpoint = "http://" . $server->getConfigGroup()->getPropertyString("anonymous-statistics.host", "stats.pocketmine.net") . "/"; + $endpoint = "http://" . $server->getConfigGroup()->getPropertyString(YmlServerProperties::ANONYMOUS_STATISTICS_HOST, "stats.pocketmine.net") . "/"; $data = []; $data["uniqueServerId"] = $server->getServerUniqueId()->toString(); diff --git a/src/updater/UpdateChecker.php b/src/updater/UpdateChecker.php index ddb0f4d66..4a3ee3444 100644 --- a/src/updater/UpdateChecker.php +++ b/src/updater/UpdateChecker.php @@ -27,6 +27,7 @@ use pocketmine\event\server\UpdateNotifyEvent; use pocketmine\Server; use pocketmine\utils\VersionString; use pocketmine\VersionInfo; +use pocketmine\YmlServerProperties; use function date; use function strtolower; use function ucfirst; @@ -43,7 +44,7 @@ class UpdateChecker{ $this->logger = new \PrefixedLogger($server->getLogger(), "Update Checker"); $this->endpoint = "http://$endpoint/api/"; - if($server->getConfigGroup()->getPropertyBool("auto-updater.enabled", true)){ + if($server->getConfigGroup()->getPropertyBool(YmlServerProperties::AUTO_UPDATER_ENABLED, true)){ $this->doCheck(); } } @@ -59,7 +60,7 @@ class UpdateChecker{ $this->checkUpdate($updateInfo); if($this->hasUpdate()){ (new UpdateNotifyEvent($this))->call(); - if($this->server->getConfigGroup()->getPropertyBool("auto-updater.on-update.warn-console", true)){ + if($this->server->getConfigGroup()->getPropertyBool(YmlServerProperties::AUTO_UPDATER_ON_UPDATE_WARN_CONSOLE, true)){ $this->showConsoleUpdate(); } }else{ @@ -157,7 +158,7 @@ class UpdateChecker{ * Returns the channel used for update checking (stable, beta, dev) */ public function getChannel() : string{ - return strtolower($this->server->getConfigGroup()->getPropertyString("auto-updater.preferred-channel", "stable")); + return strtolower($this->server->getConfigGroup()->getPropertyString(YmlServerProperties::AUTO_UPDATER_PREFERRED_CHANNEL, "stable")); } /** diff --git a/src/world/World.php b/src/world/World.php index d917a3e82..903cc60e2 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -104,6 +104,7 @@ use pocketmine\world\particle\Particle; use pocketmine\world\sound\BlockPlaceSound; use pocketmine\world\sound\Sound; use pocketmine\world\utils\SubChunkExplorer; +use pocketmine\YmlServerProperties; use function abs; use function array_filter; use function array_key_exists; @@ -513,14 +514,14 @@ class World implements ChunkManager{ $this->time = $this->provider->getWorldData()->getTime(); $cfg = $this->server->getConfigGroup(); - $this->chunkTickRadius = min($this->server->getViewDistance(), max(0, $cfg->getPropertyInt("chunk-ticking.tick-radius", 4))); + $this->chunkTickRadius = min($this->server->getViewDistance(), max(0, $cfg->getPropertyInt(YmlServerProperties::CHUNK_TICKING_TICK_RADIUS, 4))); if($cfg->getPropertyInt("chunk-ticking.per-tick", 40) <= 0){ //TODO: this needs l10n $this->logger->warning("\"chunk-ticking.per-tick\" setting is deprecated, but you've used it to disable chunk ticking. Set \"chunk-ticking.tick-radius\" to 0 in \"pocketmine.yml\" instead."); $this->chunkTickRadius = 0; } - $this->tickedBlocksPerSubchunkPerTick = $cfg->getPropertyInt("chunk-ticking.blocks-per-subchunk-per-tick", self::DEFAULT_TICKED_BLOCKS_PER_SUBCHUNK_PER_TICK); - $this->maxConcurrentChunkPopulationTasks = $cfg->getPropertyInt("chunk-generation.population-queue-size", 2); + $this->tickedBlocksPerSubchunkPerTick = $cfg->getPropertyInt(YmlServerProperties::CHUNK_TICKING_BLOCKS_PER_SUBCHUNK_PER_TICK, self::DEFAULT_TICKED_BLOCKS_PER_SUBCHUNK_PER_TICK); + $this->maxConcurrentChunkPopulationTasks = $cfg->getPropertyInt(YmlServerProperties::CHUNK_GENERATION_POPULATION_QUEUE_SIZE, 2); $this->initRandomTickBlocksFromConfig($cfg); @@ -541,7 +542,7 @@ class World implements ChunkManager{ private function initRandomTickBlocksFromConfig(ServerConfigGroup $cfg) : void{ $dontTickBlocks = []; $parser = StringToItemParser::getInstance(); - foreach($cfg->getProperty("chunk-ticking.disable-block-ticking", []) as $name){ + foreach($cfg->getProperty(YmlServerProperties::CHUNK_TICKING_DISABLE_BLOCK_TICKING, []) as $name){ $name = (string) $name; $item = $parser->parse($name); if($item !== null){ From 2912e7ca2962495aaf7e0d2e54fa13b572186055 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 25 Aug 2023 13:27:07 +0100 Subject: [PATCH 44/46] ... --- ...enerate-pocketmine-yml-property-consts.php | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/build/generate-pocketmine-yml-property-consts.php b/build/generate-pocketmine-yml-property-consts.php index b0e79519a..93d623918 100644 --- a/build/generate-pocketmine-yml-property-consts.php +++ b/build/generate-pocketmine-yml-property-consts.php @@ -62,9 +62,34 @@ if($file === false){ fwrite(STDERR, "Failed to open output file\n"); exit(1); } -fwrite($file, " $propertyName){ fwrite($file, "\tpublic const $constName = '$propertyName';\n"); From 4a0a538278edbb375d228093c3947d7a208d817d Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 25 Aug 2023 13:27:40 +0100 Subject: [PATCH 45/46] CS --- build/generate-pocketmine-yml-property-consts.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/generate-pocketmine-yml-property-consts.php b/build/generate-pocketmine-yml-property-consts.php index 93d623918..23d1de04a 100644 --- a/build/generate-pocketmine-yml-property-consts.php +++ b/build/generate-pocketmine-yml-property-consts.php @@ -36,7 +36,7 @@ if(!is_array($defaultConfig)){ $constants = []; /** - * @param mixed[] $properties + * @param mixed[] $properties * @param string[] $constants * @phpstan-param array $constants * @phpstan-param-out array $constants From 9fdb6ba5aa4df65a78684db2c8d10fc3d8a9f7b4 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 25 Aug 2023 14:02:49 +0100 Subject: [PATCH 46/46] Mark some new things as internal --- build/generate-pocketmine-yml-property-consts.php | 10 ++++++++++ src/ServerProperties.php | 4 ++++ src/YmlServerProperties.php | 6 ++++++ 3 files changed, 20 insertions(+) diff --git a/build/generate-pocketmine-yml-property-consts.php b/build/generate-pocketmine-yml-property-consts.php index 23d1de04a..90342a75f 100644 --- a/build/generate-pocketmine-yml-property-consts.php +++ b/build/generate-pocketmine-yml-property-consts.php @@ -90,6 +90,16 @@ HEADER fwrite($file, "declare(strict_types=1);\n\n"); fwrite($file, "namespace pocketmine;\n\n"); +fwrite($file, <<<'DOC' +/** + * @internal + * Constants for all properties available in pocketmine.yml. + * This is generated by build/generate-pocketmine-yml-property-consts.php. + * Do not edit this file manually. + */ + +DOC +); fwrite($file, "final class YmlServerProperties{\n"); foreach(Utils::stringifyKeys($constants) as $constName => $propertyName){ fwrite($file, "\tpublic const $constName = '$propertyName';\n"); diff --git a/src/ServerProperties.php b/src/ServerProperties.php index 884ff4f67..aad26d242 100644 --- a/src/ServerProperties.php +++ b/src/ServerProperties.php @@ -23,6 +23,10 @@ declare(strict_types=1); namespace pocketmine; +/** + * @internal + * Constants for all properties available in server.properties. + */ final class ServerProperties{ public const AUTO_SAVE = "auto-save"; diff --git a/src/YmlServerProperties.php b/src/YmlServerProperties.php index 920f0797a..7d088b13c 100644 --- a/src/YmlServerProperties.php +++ b/src/YmlServerProperties.php @@ -23,6 +23,12 @@ declare(strict_types=1); namespace pocketmine; +/** + * @internal + * Constants for all properties available in pocketmine.yml. + * This is generated by build/generate-pocketmine-yml-property-consts.php. + * Do not edit this file manually. + */ final class YmlServerProperties{ public const ALIASES = 'aliases'; public const ANONYMOUS_STATISTICS = 'anonymous-statistics';