Repository: pmmp/PocketMine-MP (language: php)

Issue:
Unable to describe new enums using RuntimeDataDescriber
<!--- tell us what you want -->
## Description
Please add a way to accept string/enum block states in RuntimeDataDescriber, or add all known states to RuntimeDataDescriber

<!--- explain why you want this and why it's a good idea -->
## Justification
String block states could not be loaded or saved
Possibly auto generate block state enums & RuntimeDataDescriber methods from BedrockData?

Affected blocks: 
- Big dripleaf
- Pointed dripstone
- Seagrass
- Structure block
- Structure void
- Sniffer egg
- Turtle egg

Blocks states:
- BlockStateStringValues::BIG_DRIPLEAF_TILT_*
- BlockStateStringValues::CRACKED_STATE_*
- BlockStateStringValues::DRIPSTONE_THICKNESS_*
- BlockStateStringValues::SEA_GRASS_TYPE_*
- BlockStateStringValues::STRUCTURE_BLOCK_TYPE_*
- BlockStateStringValues::STRUCTURE_VOID_TYPE_*
- BlockStateStringValues::TURTLE_EGG_COUNT_*

As of 1.20.10, PM 5.3
The observer block is also affected
Block states: BlockStateStringValues::MC_FACING_DIRECTION_*
<!--- (optional) describe alternative methods you've explored to achieve your goal -->
## Alternative methods
Hack!

https://github.com/diamond-gold/DummyItemsBlocks/blob/main/src/diamondgold/DummyItemsBlocks/block/hack/HackStringProperty.php

https://github.com/diamond-gold/DummyItemsBlocks/blob/main/src/diamondgold/DummyItemsBlocks/block/PointedDripstone.php
Split World->getCollisionBoxes() into multiple methods
<!--- tell us what you want -->
## Description
The `$entities` parameter is rarely used. In addition, the only reason that the `$entity` parameter is required is in case the logic controlled by `$entities` flag needs it.

Since this logic can already be separately handled using `getCollidingEntities()`, it's preferable to make `getCollisionBoxes()` look at blocks exclusively (and perhaps rename it).

<!--- (optional) describe alternative methods you've explored to achieve your goal -->
## Alternative methods



Discussion / hints:
> Possibly auto generate block state enums & RuntimeDataDescriber methods from BedrockData?

No, we specifically avoid doing this because we don't want to be bound by the Bedrock internals, which are a disastrously inconsistent mess.
There is no such thing as a "string" state in PM internals. They exist at the NBT layer to represent enums.

Not being able to represent new enums is indeed a design limitation of current RuntimeDataDescriber design, which has also caused issues in other areas. I haven't had time to explore how to solve this problem yet.
You might also consider properly implementing these blocks in a PR, instead of making a plugin to add dummy blocks.
The plugin's main aim is to solve the problem of blocks becoming update blocks and items disappearing. At the same time, allow building with these blocks.
The functionality can come later, it is a lot more work to properly implement these blocks, plus with the current backlog of PRs, it will take a while for them to make it into release.
Pm - PocketMine-MP 5.2.0 - https://github.com/pmmp/PocketMine-MP/releases/tag/5.2.0#:~:text=Releases-,5.2.0,-PocketMine%2DMP%205.2.0
Php - https://github.com/pmmp/PHP-Binaries/releases/tag/php-8.1-latest



Fix patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index c511fb815f3..1fd1731a2f4 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -152,9 +152,6 @@ jobs:
       - name: Regenerate KnownTranslation APIs
         run: php build/generate-known-translation-apis.php
 
-      - name: Regenerate RuntimeEnum(De)serializer
-        run: php build/generate-runtime-enum-serializers.php
-
       - name: Regenerate BedrockData available files constants
         run: php build/generate-bedrockdata-path-consts.php
 
diff --git a/build/generate-runtime-enum-serializers.php b/build/generate-runtime-enum-serializers.php
deleted file mode 100644
index 805ed18e126..00000000000
--- a/build/generate-runtime-enum-serializers.php
+++ /dev/null
@@ -1,265 +0,0 @@
-<?php
-
-/*
- *
- *  ____            _        _   __  __ _                  __  __ ____
- * |  _ \ ___   ___| | _____| |_|  \/  (_)_ __   ___      |  \/  |  _ \
- * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
- * |  __/ (_) | (__|   <  __/ |_| |  | | | | | |  __/_____| |  | |  __/
- * |_|   \___/ \___|_|\_\___|\__|_|  |_|_|_| |_|\___|     |_|  |_|_|
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * @author PocketMine Team
- * @link http://www.pocketmine.net/
- *
- *
- */
-
-declare(strict_types=1);
-
-namespace pocketmine\build\generate_runtime_enum_serializers;
-
-use pocketmine\block\utils\BellAttachmentType;
-use pocketmine\block\utils\CopperOxidation;
-use pocketmine\block\utils\CoralType;
-use pocketmine\block\utils\DirtType;
-use pocketmine\block\utils\DripleafState;
-use pocketmine\block\utils\DyeColor;
-use pocketmine\block\utils\FroglightType;
-use pocketmine\block\utils\LeverFacing;
-use pocketmine\block\utils\MobHeadType;
-use pocketmine\block\utils\MushroomBlockType;
-use pocketmine\block\utils\SlabType;
-use pocketmine\item\MedicineType;
-use pocketmine\item\PotionType;
-use pocketmine\item\SuspiciousStewType;
-use function array_key_first;
-use function array_keys;
-use function array_map;
-use function ceil;
-use function count;
-use function dirname;
-use function file_put_contents;
-use function implode;
-use function ksort;
-use function lcfirst;
-use function log;
-use function ob_get_clean;
-use function ob_start;
-use const SORT_STRING;
-
-require dirname(__DIR__) . '/vendor/autoload.php';
-
-/**
- * @param string[] $memberNames
- * @phpstan-param list<string> $memberNames
- *
- * @return string[]
- * @phpstan-return list<string>
- */
-function buildWriterFunc(string $virtualTypeName, string $nativeTypeName, array $memberNames, string $functionName) : array{
-	$bits = getBitsRequired($memberNames);
-	$lines = [];
-
-	$lines[] = "public function $functionName(\\$nativeTypeName &\$value) : void{";
-	$lines[] = "\t\$this->writeInt($bits, match(\$value){";
-
-	foreach($memberNames as $key => $memberName){
-		$lines[] = "\t\t$memberName => $key,";
-	}
-	$lines[] = "\t\tdefault => throw new \pocketmine\utils\AssumptionFailedError(\"All $virtualTypeName cases should be covered\")";
-	$lines[] = "\t});";
-	$lines[] = "}";
-
-	return $lines;
-}
-
-/**
- * @param string[] $memberNames
- * @phpstan-param list<string> $memberNames
- *
- * @return string[]
- * @phpstan-return list<string>
- */
-function buildReaderFunc(string $virtualTypeName, string $nativeTypeName, array $memberNames, string $functionName) : array{
-	$bits = getBitsRequired($memberNames);
-	$lines = [];
-
-	$lines[] = "public function $functionName(\\$nativeTypeName &\$value) : void{";
-	$lines[] = "\t\$value = match(\$this->readInt($bits)){";
-
-	foreach($memberNames as $key => $memberName){
-		$lines[] = "\t\t$key => $memberName,";
-	}
-	$lines[] = "\t\tdefault => throw new InvalidSerializedRuntimeDataException(\"Invalid serialized value for $virtualTypeName\")";
-	$lines[] = "\t};";
-	$lines[] = "}";
-
-	return $lines;
-}
-
-function buildInterfaceFunc(string $nativeTypeName, string $functionName) : string{
-	return "public function $functionName(\\$nativeTypeName &\$value) : void;";
-}
-
-/**
- * @param string[] $memberNames
- * @phpstan-param list<string> $memberNames
- *
- * @return string[]
- * @phpstan-return list<string>
- */
-function buildSizeCalculationFunc(string $nativeTypeName, string $functionName, array $memberNames) : array{
-	$lines = [];
-	$lines[] = "public function $functionName(\\$nativeTypeName &\$value) : void{";
-	$lines[] = "\t\$this->addBits(" . getBitsRequired($memberNames) . ");";
-	$lines[] = "}";
-
-	return $lines;
-}
-
-/**
- * @param mixed[] $members
- */
-function getBitsRequired(array $members) : int{
-	return (int) ceil(log(count($members), 2));
-}
-
-/**
- * @param object[] $members
- * @phpstan-param array<string, object> $members
- *
- * @return string[]
- * @phpstan-return list<string>
- */
-function stringifyEnumMembers(array $members, string $enumClass) : array{
-	ksort($members, SORT_STRING);
-	return array_map(fn(string $enumCaseName) => "\\$enumClass::$enumCaseName()", array_keys($members));
-}
-
-$enumsUsed = [
-	BellAttachmentType::getAll(),
-	CopperOxidation::getAll(),
-	CoralType::getAll(),
-	DirtType::getAll(),
-	DripleafState::getAll(),
-	DyeColor::getAll(),
-	FroglightType::getAll(),
-	LeverFacing::getAll(),
-	MedicineType::getAll(),
-	MushroomBlockType::getAll(),
-	MobHeadType::getAll(),
-	SlabType::getAll(),
-	SuspiciousStewType::getAll(),
-	PotionType::getAll()
-];
-
-$readerFuncs = [
-	"" => [
-		"abstract protected function readInt(int \$bits) : int;"
-	]
-];
-$writerFuncs = [
-	"" => [
-		"abstract protected function writeInt(int \$bits, int \$value) : void;"
-	]
-];
-$interfaceFuncs = [];
-$sizeCalculationFuncs = [
-	"" => [
-		"abstract protected function addBits(int \$bits) : void;"
-	]
-];
-
-foreach($enumsUsed as $enumMembers){
-	if(count($enumMembers) === 0){
-		throw new \InvalidArgumentException("Enum members cannot be empty");
-	}
-	$reflect = new \ReflectionClass($enumMembers[array_key_first($enumMembers)]);
-	$virtualTypeName = $reflect->getShortName();
-	$nativeTypeName = $reflect->getName();
-	$functionName = lcfirst($virtualTypeName);
-
-	$stringifiedMembers = stringifyEnumMembers($enumMembers, $nativeTypeName);
-	$writerFuncs[$functionName] = buildWriterFunc(
-		$virtualTypeName,
-		$nativeTypeName,
-		$stringifiedMembers,
-		$functionName
-	);
-	$readerFuncs[$functionName] = buildReaderFunc(
-		$virtualTypeName,
-		$nativeTypeName,
-		$stringifiedMembers,
-		$functionName
-	);
-	$interfaceFuncs[$functionName] = [buildInterfaceFunc(
-		$nativeTypeName,
-		$functionName
-	)];
-	$sizeCalculationFuncs[$functionName] = buildSizeCalculationFunc(
-		$nativeTypeName,
-		$functionName,
-		$stringifiedMembers
-	);
-}
-
-/**
- * @param string[][] $functions
- * @phpstan-param array<string, list<string>> $functions
- */
-function printFunctions(array $functions, string $className, string $classType) : void{
-	ksort($functions, SORT_STRING);
-
-	ob_start();
-
-	echo <<<'HEADER'
-<?php
-
-/*
- *
- *  ____            _        _   __  __ _                  __  __ ____
- * |  _ \ ___   ___| | _____| |_|  \/  (_)_ __   ___      |  \/  |  _ \
- * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
- * |  __/ (_) | (__|   <  __/ |_| |  | | | | | |  __/_____| |  | |  __/
- * |_|   \___/ \___|_|\_\___|\__|_|  |_|_|_| |_|\___|     |_|  |_|_|
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * @author PocketMine Team
- * @link http://www.pocketmine.net/
- *
- *
- */
-
-declare(strict_types=1);
-
-namespace pocketmine\data\runtime;
-
-/**
- * This class is auto-generated. Do not edit it manually.
- * @see build/generate-runtime-enum-serializers.php
- */
-
-HEADER;
-
-	echo "$classType $className{\n\n";
-	echo implode("\n\n", array_map(fn(array $functionLines) => "\t" . implode("\n\t", $functionLines), $functions));
-	echo "\n\n}\n";
-
-	file_put_contents(dirname(__DIR__) . '/src/data/runtime/' . $className . '.php', ob_get_clean());
-}
-
-printFunctions($writerFuncs, "RuntimeEnumSerializerTrait", "trait");
-printFunctions($readerFuncs, "RuntimeEnumDeserializerTrait", "trait");
-printFunctions($interfaceFuncs, "RuntimeEnumDescriber", "interface");
-printFunctions($sizeCalculationFuncs, "RuntimeEnumSizeCalculatorTrait", "trait");
-
-echo "Done. Don't forget to run CS fixup after generating code.\n";
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
index 581bf41ec5a..2fde67d6ca9 100644
--- a/phpstan.neon.dist
+++ b/phpstan.neon.dist
@@ -10,6 +10,7 @@ includes:
 	- vendor/phpstan/phpstan-strict-rules/rules.neon
 
 rules:
+	- pocketmine\phpstan\rules\DeprecatedLegacyEnumAccessRule
 	- pocketmine\phpstan\rules\DisallowEnumComparisonRule
 	- pocketmine\phpstan\rules\DisallowForeachByReferenceRule
 	- pocketmine\phpstan\rules\UnsafeForeachArrayOfStringRule
diff --git a/src/Server.php b/src/Server.php
index 2f1e9a53bd5..59a6ec645ff 100644
--- a/src/Server.php
+++ b/src/Server.php
@@ -390,7 +390,7 @@ public function getServerUniqueId() : UuidInterface{
 	}
 
 	public function getGamemode() : GameMode{
-		return GameMode::fromString($this->configGroup->getConfigString(ServerProperties::GAME_MODE, GameMode::SURVIVAL()->name())) ?? GameMode::SURVIVAL();
+		return GameMode::fromString($this->configGroup->getConfigString(ServerProperties::GAME_MODE)) ?? GameMode::SURVIVAL;
 	}
 
 	public function getForceGamemode() : bool{
@@ -817,7 +817,7 @@ public function __construct(
 					ServerProperties::ENABLE_IPV6 => true,
 					ServerProperties::WHITELIST => false,
 					ServerProperties::MAX_PLAYERS => self::DEFAULT_MAX_PLAYERS,
-					ServerProperties::GAME_MODE => GameMode::SURVIVAL()->name(),
+					ServerProperties::GAME_MODE => GameMode::SURVIVAL->name, //TODO: this probably shouldn't use the enum name directly
 					ServerProperties::FORCE_GAME_MODE => false,
 					ServerProperties::HARDCORE => false,
 					ServerProperties::PVP => true,
@@ -1019,7 +1019,7 @@ public function __construct(
 				$this->forceShutdownExit();
 				return;
 			}
-			if(!$this->enablePlugins(PluginEnableOrder::STARTUP())){
+			if(!$this->enablePlugins(PluginEnableOrder::STARTUP)){
 				$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_plugin_someEnableErrors()));
 				$this->forceShutdownExit();
 				return;
@@ -1030,7 +1030,7 @@ public function __construct(
 				return;
 			}
 
-			if(!$this->enablePlugins(PluginEnableOrder::POSTWORLD())){
+			if(!$this->enablePlugins(PluginEnableOrder::POSTWORLD)){
 				$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_plugin_someEnableErrors()));
 				$this->forceShutdownExit();
 				return;
@@ -1385,14 +1385,14 @@ public function prepareBatch(string $buffer, Compressor $compressor, ?bool $sync
 	public function enablePlugins(PluginEnableOrder $type) : bool{
 		$allSuccess = true;
 		foreach($this->pluginManager->getPlugins() as $plugin){
-			if(!$plugin->isEnabled() && $plugin->getDescription()->getOrder()->equals($type)){
+			if(!$plugin->isEnabled() && $plugin->getDescription()->getOrder() === $type){
 				if(!$this->pluginManager->enablePlugin($plugin)){
 					$allSuccess = false;
 				}
 			}
 		}
 
-		if($type->equals(PluginEnableOrder::POSTWORLD())){
+		if($type === PluginEnableOrder::POSTWORLD){
 			$this->commandMap->registerServerAliases();
 		}
 
diff --git a/src/block/AmethystCluster.php b/src/block/AmethystCluster.php
new file mode 100644
index 00000000000..639490456a3
--- /dev/null
+++ b/src/block/AmethystCluster.php
@@ -0,0 +1,133 @@
+<?php
+
+/*
+ *
+ *  ____            _        _   __  __ _                  __  __ ____
+ * |  _ \ ___   ___| | _____| |_|  \/  (_)_ __   ___      |  \/  |  _ \
+ * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
+ * |  __/ (_) | (__|   <  __/ |_| |  | | | | | |  __/_____| |  | |  __/
+ * |_|   \___/ \___|_|\_\___|\__|_|  |_|_|_| |_|\___|     |_|  |_|_|
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * @author PocketMine Team
+ * @link http://www.pocketmine.net/
+ *
+ *
+ */
+
+declare(strict_types=1);
+
+namespace pocketmine\block;
+
+use pocketmine\block\utils\AmethystTrait;
+use pocketmine\block\utils\AnyFacingTrait;
+use pocketmine\block\utils\FortuneDropHelper;
+use pocketmine\block\utils\SupportType;
+use pocketmine\data\runtime\RuntimeDataDescriber;
+use pocketmine\item\Item;
+use pocketmine\item\VanillaItems;
+use pocketmine\math\Axis;
+use pocketmine\math\AxisAlignedBB;
+use pocketmine\math\Facing;
+use pocketmine\math\Vector3;
+use pocketmine\player\Player;
+use pocketmine\utils\AssumptionFailedError;
+use pocketmine\world\BlockTransaction;
+
+final class AmethystCluster extends Transparent{
+	use AmethystTrait;
+	use AnyFacingTrait;
+
+	public const STAGE_SMALL_BUD = 0;
+	public const STAGE_MEDIUM_BUD = 1;
+	public const STAGE_LARGE_BUD = 2;
+	public const STAGE_CLUSTER = 3;
+
+	private int $stage = self::STAGE_CLUSTER;
+
+	public function describeBlockItemState(RuntimeDataDescriber $w) : void{
+		$w->boundedIntAuto(self::STAGE_SMALL_BUD, self::STAGE_CLUSTER, $this->stage);
+	}
+
+	public function getStage() : int{ return $this->stage; }
+
+	public function setStage(int $stage) : self{
+		if($stage < self::STAGE_SMALL_BUD || $stage > self::STAGE_CLUSTER){
+			throw new \InvalidArgumentException("Size must be in range " . self::STAGE_SMALL_BUD . " ... " . self::STAGE_CLUSTER);
+		}
+		$this->stage = $stage;
+		return $this;
+	}
+
+	public function getLightLevel() : int{
+		return match($this->stage){
+			self::STAGE_SMALL_BUD => 1,
+			self::STAGE_MEDIUM_BUD => 2,
+			self::STAGE_LARGE_BUD => 4,
+			self::STAGE_CLUSTER => 5,
+			default => throw new AssumptionFailedError("Invalid stage $this->stage"),
+		};
+	}
+
+	protected function recalculateCollisionBoxes() : array{
+		$myAxis = Facing::axis($this->facing);
+
+		$box = AxisAlignedBB::one();
+		foreach([Axis::Y, Axis::Z, Axis::X] as $axis){
+			if($axis === $myAxis){
+				continue;
+			}
+			$box->squash($axis, $this->stage === self::STAGE_SMALL_BUD ? 4 / 16 : 3 / 16);
+		}
+		$box->trim($this->facing, 1 - ($this->stage === self::STAGE_CLUSTER ? 7 / 16 : ($this->stage + 3) / 16));
+
+		return [$box];
+	}
+
+	private function canBeSupportedAt(Block $block, int $facing) : bool{
+		return $block->getAdjacentSupportType($facing) === SupportType::FULL;
+	}
+
+	public function getSupportType(int $facing) : SupportType{
+		return SupportType::NONE;
+	}
+
+	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
+		if(!$this->canBeSupportedAt($blockReplace, Facing::opposite($face))){
+			return false;
+		}
+
+		$this->facing = $face;
+		return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
+	}
+
+	public function onNearbyBlockChange() : void{
+		if(!$this->canBeSupportedAt($this, Facing::opposite($this->facing))){
+			$this->position->getWorld()->useBreakOn($this->position);
+		}
+	}
+
+	public function isAffectedBySilkTouch() : bool{
+		return true;
+	}
+
+	public function getDropsForCompatibleTool(Item $item) : array{
+		if($this->stage === self::STAGE_CLUSTER){
+			return [VanillaItems::AMETHYST_SHARD()->setCount(FortuneDropHelper::weighted($item, min: 4, maxBase: 4))];
+		}
+
+		return [];
+	}
+
+	public function getDropsForIncompatibleTool(Item $item) : array{
+		if($this->stage === self::STAGE_CLUSTER){
+			return [VanillaItems::AMETHYST_SHARD()->setCount(FortuneDropHelper::weighted($item, min: 2, maxBase: 2))];
+		}
+
+		return [];
+	}
+}
diff --git a/src/block/Anvil.php b/src/block/Anvil.php
index de8b6d33cc9..916c390ef6d 100644
--- a/src/block/Anvil.php
+++ b/src/block/Anvil.php
@@ -52,7 +52,7 @@ class Anvil extends Transparent implements Fallable{
 	private int $damage = self::UNDAMAGED;
 
 	public function describeBlockItemState(RuntimeDataDescriber $w) : void{
-		$w->boundedInt(2, self::UNDAMAGED, self::VERY_DAMAGED, $this->damage);
+		$w->boundedIntAuto(self::UNDAMAGED, self::VERY_DAMAGED, $this->damage);
 	}
 
 	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
@@ -78,7 +78,7 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
diff --git a/src/block/Bamboo.php b/src/block/Bamboo.php
index 980f4382d13..5df71f69681 100644
--- a/src/block/Bamboo.php
+++ b/src/block/Bamboo.php
@@ -23,6 +23,7 @@
 
 namespace pocketmine\block;
 
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\block\utils\SupportType;
 use pocketmine\data\runtime\RuntimeDataDescriber;
 use pocketmine\event\block\StructureGrowEvent;
@@ -46,6 +47,7 @@
 use const PHP_INT_MAX;
 
 class Bamboo extends Transparent{
+	use StaticSupportTrait;
 
 	public const NO_LEAVES = 0;
 	public const SMALL_LEAVES = 1;
@@ -56,7 +58,7 @@ class Bamboo extends Transparent{
 	protected int $leafSize = self::NO_LEAVES;
 
 	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
-		$w->boundedInt(2, self::NO_LEAVES, self::LARGE_LEAVES, $this->leafSize);
+		$w->boundedIntAuto(self::NO_LEAVES, self::LARGE_LEAVES, $this->leafSize);
 		$w->bool($this->thick);
 		$w->bool($this->ready);
 	}
@@ -95,7 +97,7 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	private static function getOffsetSeed(int $x, int $y, int $z) : int{
@@ -120,12 +122,14 @@ public function getModelPositionOffset() : ?Vector3{
 		return new Vector3($retX, 0, $retZ);
 	}
 
-	private function canBeSupportedBy(Block $block) : bool{
+	private function canBeSupportedAt(Block $block) : bool{
+		$supportBlock = $block->getSide(Facing::DOWN);
 		return
-			$block->getTypeId() === BlockTypeIds::GRAVEL ||
-			$block->hasTypeTag(BlockTypeTags::DIRT) ||
-			$block->hasTypeTag(BlockTypeTags::MUD) ||
-			$block->hasTypeTag(BlockTypeTags::SAND);
+			$supportBlock->hasSameTypeId($this) ||
+			$supportBlock->getTypeId() === BlockTypeIds::GRAVEL ||
+			$supportBlock->hasTypeTag(BlockTypeTags::DIRT) ||
+			$supportBlock->hasTypeTag(BlockTypeTags::MUD) ||
+			$supportBlock->hasTypeTag(BlockTypeTags::SAND);
 	}
 
 	private function seekToTop() : Bamboo{
@@ -153,14 +157,6 @@ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player
 		return false;
 	}
 
-	public function onNearbyBlockChange() : void{
-		$world = $this->position->getWorld();
-		$below = $world->getBlock($this->position->down());
-		if(!$this->canBeSupportedBy($below) && !$below->hasSameTypeId($this)){
-			$world->useBreakOn($this->position);
-		}
-	}
-
 	private function grow(int $maxHeight, int $growAmount, ?Player $player) : bool{
 		$world = $this->position->getWorld();
 		if(!$world->getBlock($this->position->up())->canBeReplaced()){
diff --git a/src/block/BambooSapling.php b/src/block/BambooSapling.php
index 6be42546ab6..67c8a24e0b0 100644
--- a/src/block/BambooSapling.php
+++ b/src/block/BambooSapling.php
@@ -23,17 +23,21 @@
 
 namespace pocketmine\block;
 
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\data\runtime\RuntimeDataDescriber;
 use pocketmine\event\block\StructureGrowEvent;
 use pocketmine\item\Bamboo as ItemBamboo;
 use pocketmine\item\Fertilizer;
 use pocketmine\item\Item;
 use pocketmine\item\VanillaItems;
+use pocketmine\math\Facing;
 use pocketmine\math\Vector3;
 use pocketmine\player\Player;
 use pocketmine\world\BlockTransaction;
 
 final class BambooSapling extends Flowable{
+	use StaticSupportTrait;
+
 	private bool $ready = false;
 
 	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
@@ -48,19 +52,13 @@ public function setReady(bool $ready) : self{
 		return $this;
 	}
 
-	private function canBeSupportedBy(Block $block) : bool{
+	private function canBeSupportedAt(Block $block) : bool{
+		$supportBlock = $block->getSide(Facing::DOWN);
 		return
-			$block->getTypeId() === BlockTypeIds::GRAVEL ||
-			$block->hasTypeTag(BlockTypeTags::DIRT) ||
-			$block->hasTypeTag(BlockTypeTags::MUD) ||
-			$block->hasTypeTag(BlockTypeTags::SAND);
-	}
-
-	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		if(!$this->canBeSupportedBy($blockReplace->position->getWorld()->getBlock($blockReplace->position->down()))){
-			return false;
-		}
-		return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
+			$supportBlock->getTypeId() === BlockTypeIds::GRAVEL ||
+			$supportBlock->hasTypeTag(BlockTypeTags::DIRT) ||
+			$supportBlock->hasTypeTag(BlockTypeTags::MUD) ||
+			$supportBlock->hasTypeTag(BlockTypeTags::SAND);
 	}
 
 	public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
@@ -73,13 +71,6 @@ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player
 		return false;
 	}
 
-	public function onNearbyBlockChange() : void{
-		$world = $this->position->getWorld();
-		if(!$this->canBeSupportedBy($world->getBlock($this->position->down()))){
-			$world->useBreakOn($this->position);
-		}
-	}
-
 	private function grow(?Player $player) : bool{
 		$world = $this->position->getWorld();
 		if(!$world->getBlock($this->position->up())->canBeReplaced()){
diff --git a/src/block/BaseBanner.php b/src/block/BaseBanner.php
index fdfad3b0956..c2bbaf737a6 100644
--- a/src/block/BaseBanner.php
+++ b/src/block/BaseBanner.php
@@ -26,7 +26,6 @@
 use pocketmine\block\tile\Banner as TileBanner;
 use pocketmine\block\utils\BannerPatternLayer;
 use pocketmine\block\utils\ColoredTrait;
-use pocketmine\block\utils\DyeColor;
 use pocketmine\block\utils\SupportType;
 use pocketmine\item\Banner as ItemBanner;
 use pocketmine\item\Item;
@@ -48,11 +47,6 @@ abstract class BaseBanner extends Transparent{
 	 */
 	protected array $patterns = [];
 
-	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->color = DyeColor::BLACK();
-		parent::__construct($idInfo, $name, $typeInfo);
-	}
-
 	public function readStateFromWorld() : Block{
 		parent::readStateFromWorld();
 		$tile = $this->position->getWorld()->getTile($this->position);
@@ -111,7 +105,7 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	private function canBeSupportedBy(Block $block) : bool{
diff --git a/src/block/BaseBigDripleaf.php b/src/block/BaseBigDripleaf.php
index dcd81af0abd..b2547447c28 100644
--- a/src/block/BaseBigDripleaf.php
+++ b/src/block/BaseBigDripleaf.php
@@ -131,6 +131,6 @@ public function getFlammability() : int{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 }
diff --git a/src/block/BaseCake.php b/src/block/BaseCake.php
index 21fd6336a06..4b390384026 100644
--- a/src/block/BaseCake.php
+++ b/src/block/BaseCake.php
@@ -23,6 +23,7 @@
 
 namespace pocketmine\block;
 
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\block\utils\SupportType;
 use pocketmine\entity\effect\EffectInstance;
 use pocketmine\entity\FoodSource;
@@ -31,27 +32,16 @@
 use pocketmine\math\Facing;
 use pocketmine\math\Vector3;
 use pocketmine\player\Player;
-use pocketmine\world\BlockTransaction;
 
 abstract class BaseCake extends Transparent implements FoodSource{
+	use StaticSupportTrait;
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
-	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		$down = $this->getSide(Facing::DOWN);
-		if($down->getTypeId() !== BlockTypeIds::AIR){
-			return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
-		}
-
-		return false;
-	}
-
-	public function onNearbyBlockChange() : void{
-		if($this->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::AIR){ //Replace with common break method
-			$this->position->getWorld()->useBreakOn($this->position);
-		}
+	private function canBeSupportedAt(Block $block) : bool{
+		return $block->getSide(Facing::DOWN)->getTypeId() !== BlockTypeIds::AIR;
 	}
 
 	public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
diff --git a/src/block/BaseCoral.php b/src/block/BaseCoral.php
index 3a6acd5d15d..b9c595a97d4 100644
--- a/src/block/BaseCoral.php
+++ b/src/block/BaseCoral.php
@@ -24,7 +24,6 @@
 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\item\Item;
@@ -33,11 +32,6 @@
 abstract class BaseCoral extends Transparent{
 	use CoralTypeTrait;
 
-	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->coralType = CoralType::TUBE();
-		parent::__construct($idInfo, $name, $typeInfo);
-	}
-
 	public function onNearbyBlockChange() : void{
 		if(!$this->dead){
 			$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(40, 200));
@@ -78,6 +72,6 @@ protected function isCoveredWithWater() : bool{
 	protected function recalculateCollisionBoxes() : array{ return []; }
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 }
diff --git a/src/block/BaseSign.php b/src/block/BaseSign.php
index d7a22f3b8ab..407ab1ada14 100644
--- a/src/block/BaseSign.php
+++ b/src/block/BaseSign.php
@@ -103,7 +103,7 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	abstract protected function getSupportingFace() : int;
@@ -172,13 +172,13 @@ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player
 		}
 
 		$dyeColor = $item instanceof Dye ? $item->getColor() : match($item->getTypeId()){
-			ItemTypeIds::BONE_MEAL => DyeColor::WHITE(),
-			ItemTypeIds::LAPIS_LAZULI => DyeColor::BLUE(),
-			ItemTypeIds::COCOA_BEANS => DyeColor::BROWN(),
+			ItemTypeIds::BONE_MEAL => DyeColor::WHITE,
+			ItemTypeIds::LAPIS_LAZULI => DyeColor::BLUE,
+			ItemTypeIds::COCOA_BEANS => DyeColor::BROWN,
 			default => null
 		};
 		if($dyeColor !== null){
-			$color = $dyeColor->equals(DyeColor::BLACK()) ? new Color(0, 0, 0) : $dyeColor->getRgbValue();
+			$color = $dyeColor === DyeColor::BLACK ? new Color(0, 0, 0) : $dyeColor->getRgbValue();
 			if(
 				$color->toARGB() !== $this->text->getBaseColor()->toARGB() &&
 				$this->doSignChange(new SignText($this->text->getLines(), $color, $this->text->isGlowing()), $player, $item)
diff --git a/src/block/Bed.php b/src/block/Bed.php
index 312b21cd12a..d4dca17d62f 100644
--- a/src/block/Bed.php
+++ b/src/block/Bed.php
@@ -48,11 +48,6 @@ class Bed extends Transparent{
 	protected bool $occupied = false;
 	protected bool $head = false;
 
-	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->color = DyeColor::RED();
-		parent::__construct($idInfo, $name, $typeInfo);
-	}
-
 	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
 		$w->horizontalFacing($this->facing);
 		$w->bool($this->occupied);
@@ -65,6 +60,8 @@ public function readStateFromWorld() : Block{
 		$tile = $this->position->getWorld()->getTile($this->position);
 		if($tile instanceof TileBed){
 			$this->color = $tile->getColor();
+		}else{
+			$this->color = DyeColor::RED; //legacy pre-1.1 beds don't have tiles
 		}
 
 		return $this;
@@ -87,7 +84,7 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	public function isHeadPart() : bool{
@@ -209,7 +206,7 @@ public function getAffectedBlocks() : array{
 	}
 
 	private function canBeSupportedAt(Block $block) : bool{
-		return !$block->getAdjacentSupportType(Facing::DOWN)->equals(SupportType::NONE());
+		return $block->getAdjacentSupportType(Facing::DOWN) !== SupportType::NONE;
 	}
 
 	public function getMaxStackSize() : int{ return 1; }
diff --git a/src/block/Bell.php b/src/block/Bell.php
index 3f15d6909dc..ec033cef87a 100644
--- a/src/block/Bell.php
+++ b/src/block/Bell.php
@@ -35,32 +35,26 @@
 use pocketmine\math\RayTraceResult;
 use pocketmine\math\Vector3;
 use pocketmine\player\Player;
-use pocketmine\utils\AssumptionFailedError;
 use pocketmine\world\BlockTransaction;
 use pocketmine\world\sound\BellRingSound;
 
 final class Bell extends Transparent{
 	use HorizontalFacingTrait;
 
-	private BellAttachmentType $attachmentType;
-
-	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->attachmentType = BellAttachmentType::FLOOR();
-		parent::__construct($idInfo, $name, $typeInfo);
-	}
+	private BellAttachmentType $attachmentType = BellAttachmentType::FLOOR;
 
 	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
-		$w->bellAttachmentType($this->attachmentType);
+		$w->enum($this->attachmentType);
 		$w->horizontalFacing($this->facing);
 	}
 
 	protected function recalculateCollisionBoxes() : array{
-		if($this->attachmentType->equals(BellAttachmentType::FLOOR())){
+		if($this->attachmentType === BellAttachmentType::FLOOR){
 			return [
 				AxisAlignedBB::one()->squash(Facing::axis($this->facing), 1 / 4)->trim(Facing::UP, 3 / 16)
 			];
 		}
-		if($this->attachmentType->equals(BellAttachmentType::CEILING())){
+		if($this->attachmentType === BellAttachmentType::CEILING){
 			return [
 				AxisAlignedBB::one()->contract(1 / 4, 0, 1 / 4)->trim(Facing::DOWN, 1 / 4)
 			];
@@ -72,12 +66,12 @@ protected function recalculateCollisionBoxes() : array{
 			->trim(Facing::DOWN, 1 / 4);
 
 		return [
-			$this->attachmentType->equals(BellAttachmentType::ONE_WALL()) ? $box->trim($this->facing, 3 / 16) : $box
+			$this->attachmentType === BellAttachmentType::ONE_WALL ? $box->trim($this->facing, 3 / 16) : $box
 		];
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	public function getAttachmentType() : BellAttachmentType{ return $this->attachmentType; }
@@ -89,7 +83,7 @@ public function setAttachmentType(BellAttachmentType $attachmentType) : self{
 	}
 
 	private function canBeSupportedAt(Block $block, int $face) : bool{
-		return !$block->getAdjacentSupportType($face)->equals(SupportType::NONE());
+		return $block->getAdjacentSupportType($face) !== SupportType::NONE;
 	}
 
 	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
@@ -100,15 +94,15 @@ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Blo
 			if($player !== null){
 				$this->setFacing(Facing::opposite($player->getHorizontalFacing()));
 			}
-			$this->setAttachmentType(BellAttachmentType::FLOOR());
+			$this->setAttachmentType(BellAttachmentType::FLOOR);
 		}elseif($face === Facing::DOWN){
-			$this->setAttachmentType(BellAttachmentType::CEILING());
+			$this->setAttachmentType(BellAttachmentType::CEILING);
 		}else{
 			$this->setFacing($face);
 			$this->setAttachmentType(
 				$this->canBeSupportedAt($blockReplace, $face) ?
-					BellAttachmentType::TWO_WALLS() :
-					BellAttachmentType::ONE_WALL()
+					BellAttachmentType::TWO_WALLS :
+					BellAttachmentType::ONE_WALL
 			);
 		}
 		return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
@@ -116,11 +110,10 @@ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Blo
 
 	public function onNearbyBlockChange() : void{
 		foreach(match($this->attachmentType){
-			BellAttachmentType::CEILING() => [Facing::UP],
-			BellAttachmentType::FLOOR() => [Facing::DOWN],
-			BellAttachmentType::ONE_WALL() => [Facing::opposite($this->facing)],
-			BellAttachmentType::TWO_WALLS() => [$this->facing, Facing::opposite($this->facing)],
-			default => throw new AssumptionFailedError("All cases of BellAttachmentType must be handled")
+			BellAttachmentType::CEILING => [Facing::UP],
+			BellAttachmentType::FLOOR => [Facing::DOWN],
+			BellAttachmentType::ONE_WALL => [Facing::opposite($this->facing)],
+			BellAttachmentType::TWO_WALLS => [$this->facing, Facing::opposite($this->facing)]
 		} as $supportBlockDirection){
 			if(!$this->canBeSupportedAt($this, $supportBlockDirection)){
 				$this->position->getWorld()->useBreakOn($this->position);
@@ -159,10 +152,9 @@ public function ring(int $faceHit) : void{
 
 	private function isValidFaceToRing(int $faceHit) : bool{
 		return match($this->attachmentType){
-			BellAttachmentType::CEILING() => true,
-			BellAttachmentType::FLOOR() => Facing::axis($faceHit) === Facing::axis($this->facing),
-			BellAttachmentType::ONE_WALL(), BellAttachmentType::TWO_WALLS() => $faceHit === Facing::rotateY($this->facing, false) || $faceHit === Facing::rotateY($this->facing, true),
-			default => throw new AssumptionFailedError("All cases of BellAttachmentType must be handled")
+			BellAttachmentType::CEILING => true,
+			BellAttachmentType::FLOOR => Facing::axis($faceHit) === Facing::axis($this->facing),
+			BellAttachmentType::ONE_WALL, BellAttachmentType::TWO_WALLS => $faceHit === Facing::rotateY($this->facing, false) || $faceHit === Facing::rotateY($this->facing, true),
 		};
 	}
 }
diff --git a/src/block/BigDripleafHead.php b/src/block/BigDripleafHead.php
index d5bd226ca1c..a9b87bf7f76 100644
--- a/src/block/BigDripleafHead.php
+++ b/src/block/BigDripleafHead.php
@@ -30,22 +30,16 @@
 use pocketmine\math\AxisAlignedBB;
 use pocketmine\math\Facing;
 use pocketmine\math\RayTraceResult;
-use pocketmine\utils\AssumptionFailedError;
 use pocketmine\world\sound\DripleafTiltDownSound;
 use pocketmine\world\sound\DripleafTiltUpSound;
 
 class BigDripleafHead extends BaseBigDripleaf{
 
-	protected DripleafState $leafState;
-
-	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->leafState = DripleafState::STABLE();
-		parent::__construct($idInfo, $name, $typeInfo);
-	}
+	protected DripleafState $leafState = DripleafState::STABLE;
 
 	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
 		parent::describeBlockOnlyState($w);
-		$w->dripleafState($this->leafState);
+		$w->enum($this->leafState);
 	}
 
 	protected function isHead() : bool{
@@ -76,20 +70,20 @@ private function setTiltAndScheduleTick(DripleafState $tilt) : void{
 
 	private function getLeafTopOffset() : float{
 		return match($this->leafState){
-			DripleafState::STABLE(), DripleafState::UNSTABLE() => 1 / 16,
-			DripleafState::PARTIAL_TILT() => 3 / 16,
+			DripleafState::STABLE, DripleafState::UNSTABLE => 1 / 16,
+			DripleafState::PARTIAL_TILT => 3 / 16,
 			default => 0
 		};
 	}
 
 	public function onEntityInside(Entity $entity) : bool{
-		if(!$entity instanceof Projectile && $this->leafState->equals(DripleafState::STABLE())){
+		if(!$entity instanceof Projectile && $this->leafState === DripleafState::STABLE){
 			//the entity must be standing on top of the leaf - do not collapse if the entity is standing underneath
 			$intersection = AxisAlignedBB::one()
 				->offset($this->position->x, $this->position->y, $this->position->z)
 				->trim(Facing::DOWN, 1 - $this->getLeafTopOffset());
 			if($entity->getBoundingBox()->intersectsWith($intersection)){
-				$this->setTiltAndScheduleTick(DripleafState::UNSTABLE());
+				$this->setTiltAndScheduleTick(DripleafState::UNSTABLE);
 				return false;
 			}
 		}
@@ -97,22 +91,21 @@ public function onEntityInside(Entity $entity) : bool{
 	}
 
 	public function onProjectileHit(Projectile $projectile, RayTraceResult $hitResult) : void{
-		if(!$this->leafState->equals(DripleafState::FULL_TILT())){
-			$this->setTiltAndScheduleTick(DripleafState::FULL_TILT());
+		if($this->leafState !== DripleafState::FULL_TILT){
+			$this->setTiltAndScheduleTick(DripleafState::FULL_TILT);
 			$this->position->getWorld()->addSound($this->position, new DripleafTiltDownSound());
 		}
 	}
 
 	public function onScheduledUpdate() : void{
-		if(!$this->leafState->equals(DripleafState::STABLE())){
-			if($this->leafState->equals(DripleafState::FULL_TILT())){
-				$this->position->getWorld()->setBlock($this->position, $this->setLeafState(DripleafState::STABLE()));
+		if($this->leafState !== DripleafState::STABLE){
+			if($this->leafState === DripleafState::FULL_TILT){
+				$this->position->getWorld()->setBlock($this->position, $this->setLeafState(DripleafState::STABLE));
 				$this->position->getWorld()->addSound($this->position, new DripleafTiltUpSound());
 			}else{
-				$this->setTiltAndScheduleTick(match($this->leafState->id()){
-					DripleafState::UNSTABLE()->id() => DripleafState::PARTIAL_TILT(),
-					DripleafState::PARTIAL_TILT()->id() => DripleafState::FULL_TILT(),
-					default => throw new AssumptionFailedError("All types should be covered")
+				$this->setTiltAndScheduleTick(match($this->leafState){
+					DripleafState::UNSTABLE => DripleafState::PARTIAL_TILT,
+					DripleafState::PARTIAL_TILT => DripleafState::FULL_TILT,
 				});
 				$this->position->getWorld()->addSound($this->position, new DripleafTiltDownSound());
 			}
@@ -120,7 +113,7 @@ public function onScheduledUpdate() : void{
 	}
 
 	protected function recalculateCollisionBoxes() : array{
-		if(!$this->leafState->equals(DripleafState::FULL_TILT())){
+		if($this->leafState !== DripleafState::FULL_TILT){
 			return [
 				AxisAlignedBB::one()
 					->trim(Facing::DOWN, 11 / 16)
diff --git a/src/block/Block.php b/src/block/Block.php
index 9f61982ff73..5d87db8797b 100644
--- a/src/block/Block.php
+++ b/src/block/Block.php
@@ -42,7 +42,6 @@
 use pocketmine\item\enchantment\VanillaEnchantments;
 use pocketmine\item\Item;
 use pocketmine\item\ItemBlock;
-use pocketmine\math\Axis;
 use pocketmine\math\AxisAlignedBB;
 use pocketmine\math\Facing;
 use pocketmine\math\RayTraceResult;
@@ -50,22 +49,26 @@
 use pocketmine\nbt\tag\CompoundTag;
 use pocketmine\player\Player;
 use pocketmine\utils\AssumptionFailedError;
+use pocketmine\utils\Binary;
 use pocketmine\world\BlockTransaction;
 use pocketmine\world\format\Chunk;
 use pocketmine\world\Position;
 use pocketmine\world\World;
 use function count;
 use function get_class;
+use function hash;
 use const PHP_INT_MAX;
 
 class Block{
-	public const INTERNAL_STATE_DATA_BITS = 8;
+	public const INTERNAL_STATE_DATA_BITS = 11;
 	public const INTERNAL_STATE_DATA_MASK = ~(~0 << self::INTERNAL_STATE_DATA_BITS);
 
 	/**
 	 * @internal
+	 * Hardcoded int is `Binary::readLong(hash('xxh3', Binary::writeLLong(BlockTypeIds::AIR), binary: true))`
+	 * TODO: it would be much easier if we could just make this 0 or some other easy value
 	 */
-	public const EMPTY_STATE_ID = (BlockTypeIds::AIR << self::INTERNAL_STATE_DATA_BITS) | (BlockTypeIds::AIR & self::INTERNAL_STATE_DATA_MASK);
+	public const EMPTY_STATE_ID = (BlockTypeIds::AIR << self::INTERNAL_STATE_DATA_BITS) | (-7482769108513497636 & self::INTERNAL_STATE_DATA_MASK);
 
 	protected BlockIdentifier $idInfo;
 	protected string $fallbackName;
@@ -80,6 +83,23 @@ class Block{
 
 	private Block $defaultState;
 
+	private int $stateIdXorMask;
+
+	/**
+	 * Computes the mask to be XOR'd with the state data.
+	 * This is to improve distribution of the state data bits, which occupy the least significant bits of the state ID.
+	 * Improved distribution improves PHP array performance when using the state ID as a key, as PHP arrays use some of
+	 * the lower bits of integer keys directly without hashing.
+	 *
+	 * The type ID is included in the XOR mask. This is not necessary to improve distribution, but it reduces the number
+	 * of operations required to compute the state ID (micro optimization).
+	 */
+	private static function computeStateIdXorMask(int $typeId) : int{
+		return
+			$typeId << self::INTERNAL_STATE_DATA_BITS |
+			(Binary::readLong(hash('xxh3', Binary::writeLLong($typeId), binary: true)) & self::INTERNAL_STATE_DATA_MASK);
+	}
+
 	/**
 	 * @param string $name English name of the block type (TODO: implement translations)
 	 */
@@ -97,6 +117,9 @@ public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo
 		$this->describeBlockOnlyState($calculator);
 		$this->requiredBlockOnlyStateDataBits = $calculator->getBitsUsed();
 
+		$this->stateIdXorMask = self::computeStateIdXorMask($idInfo->getBlockTypeId());
+
+		//this must be done last, otherwise the defaultState could have uninitialized fields
 		$defaultState = clone $this;
 		$this->defaultState = $defaultState;
 		$defaultState->defaultState = $defaultState;
@@ -152,13 +175,7 @@ public function getTypeId() : int{
 	 * {@link RuntimeBlockStateRegistry::fromStateId()}.
 	 */
 	public function getStateId() : int{
-		$typeId = $this->getTypeId();
-		//TODO: this XOR mask improves hashtable distribution, but it's only effective if the number of unique block
-		//type IDs is larger than the number of available state data bits. We should probably hash (e.g. using xxhash)
-		//the type ID to create a better mask.
-		//Alternatively, we could hash the whole state ID, but this is currently problematic, since we currently need
-		//to be able to recover the state data from the state ID because of UnknownBlock.
-		return ($typeId << self::INTERNAL_STATE_DATA_BITS) | ($this->encodeFullState() ^ ($typeId & self::INTERNAL_STATE_DATA_MASK));
+		return $this->encodeFullState() ^ $this->stateIdXorMask;
 	}
 
 	/**
@@ -228,12 +245,6 @@ private function decodeBlockOnlyState(int $data) : void{
 		}
 	}
 
-	private function decodeFullState(int $data) : void{
-		$reader = new RuntimeDataReader($this->requiredBlockItemStateDataBits + $this->requiredBlockOnlyStateDataBits, $data);
-		$this->decodeBlockItemState($reader->readInt($this->requiredBlockItemStateDataBits));
-		$this->decodeBlockOnlyState($reader->readInt($this->requiredBlockOnlyStateDataBits));
-	}
-
 	private function encodeBlockItemState() : int{
 		$writer = new RuntimeDataWriter($this->requiredBlockItemStateDataBits);
 
@@ -304,18 +315,32 @@ public function generateStatePermutations() : \Generator{
 		if($bits > Block::INTERNAL_STATE_DATA_BITS){
 			throw new \LogicException("Block state data cannot use more than " . Block::INTERNAL_STATE_DATA_BITS . " bits");
 		}
-		for($stateData = 0; $stateData < (1 << $bits); ++$stateData){
-			$v = clone $this;
+		for($blockItemStateData = 0; $blockItemStateData < (1 << $this->requiredBlockItemStateDataBits); ++$blockItemStateData){
+			$withType = clone $this;
 			try{
-				$v->decodeFullState($stateData);
-				if($v->encodeFullState() !== $stateData){
-					throw new \LogicException(static::class . "::decodeStateData() accepts invalid state data (returned " . $v->encodeFullState() . " for input $stateData)");
+				$withType->decodeBlockItemState($blockItemStateData);
+				$encoded = $withType->encodeBlockItemState();
+				if($encoded !== $blockItemStateData){
+					throw new \LogicException(static::class . "::decodeBlockItemState() accepts invalid inputs (returned $encoded for input $blockItemStateData)");
 				}
 			}catch(InvalidSerializedRuntimeDataException){ //invalid property combination, leave it
 				continue;
 			}
 
-			yield $v;
+			for($blockOnlyStateData = 0; $blockOnlyStateData < (1 << $this->requiredBlockOnlyStateDataBits); ++$blockOnlyStateData){
+				$withState = clone $withType;
+				try{
+					$withState->decodeBlockOnlyState($blockOnlyStateData);
+					$encoded = $withState->encodeBlockOnlyState();
+					if($encoded !== $blockOnlyStateData){
+						throw new \LogicException(static::class . "::decodeBlockOnlyState() accepts invalid inputs (returned $encoded for input $blockOnlyStateData)");
+					}
+				}catch(InvalidSerializedRuntimeDataException){ //invalid property combination, leave it
+					continue;
+				}
+
+				yield $withState;
+			}
 		}
 	}
 
@@ -721,8 +746,14 @@ public function onIncinerate() : void{
 	 * @return Block
 	 */
 	public function getSide(int $side, int $step = 1){
-		if($this->position->isValid()){
-			return $this->position->getWorld()->getBlock($this->position->getSide($side, $step));
+		$position = $this->position;
+		if($position->isValid()){
+			[$dx, $dy, $dz] = Facing::OFFSET[$side] ?? [0, 0, 0];
+			return $position->getWorld()->getBlockAt(
+				$position->x + ($dx * $step),
+				$position->y + ($dy * $step),
+				$position->z + ($dz * $step)
+			);
 		}
 
 		throw new \LogicException("Block does not have a valid world");
@@ -736,8 +767,14 @@ public function getSide(int $side, int $step = 1){
 	 */
 	public function getHorizontalSides() : \Generator{
 		$world = $this->position->getWorld();
-		foreach($this->position->sidesAroundAxis(Axis::Y) as $vector3){
-			yield $world->getBlock($vector3);
+		foreach(Facing::HORIZONTAL as $facing){
+			[$dx, $dy, $dz] = Facing::OFFSET[$facing];
+			//TODO: yield Facing as the key?
+			yield $world->getBlockAt(
+				$this->position->x + $dx,
+				$this->position->y + $dy,
+				$this->position->z + $dz
+			);
 		}
 	}
 
@@ -749,8 +786,13 @@ public function getHorizontalSides() : \Generator{
 	 */
 	public function getAllSides() : \Generator{
 		$world = $this->position->getWorld();
-		foreach($this->position->sides() as $vector3){
-			yield $world->getBlock($vector3);
+		foreach(Facing::OFFSET as [$dx, $dy, $dz]){
+			//TODO: yield Facing as the key?
+			yield $world->getBlockAt(
+				$this->position->x + $dx,
+				$this->position->y + $dy,
+				$this->position->z + $dz
+			);
 		}
 	}
 
@@ -877,7 +919,7 @@ protected function recalculateCollisionBoxes() : array{
 	 * blocks placed on the given face can be supported by this block.
 	 */
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::FULL();
+		return SupportType::FULL;
 	}
 
 	protected function getAdjacentSupportType(int $facing) : SupportType{
diff --git a/src/block/BlockTypeIds.php b/src/block/BlockTypeIds.php
index dc86490c332..dec31eff11f 100644
--- a/src/block/BlockTypeIds.php
+++ b/src/block/BlockTypeIds.php
@@ -562,10 +562,10 @@ private function __construct(){
 	public const WEIGHTED_PRESSURE_PLATE_HEAVY = 10532;
 	public const WEIGHTED_PRESSURE_PLATE_LIGHT = 10533;
 	public const WHEAT = 10534;
-
+	public const BUDDING_AMETHYST = 10535;
 	public const WHITE_TULIP = 10536;
 	public const WOOL = 10537;
-
+	public const AMETHYST_CLUSTER = 10538;
 	public const GLAZED_TERRACOTTA = 10539;
 	public const AMETHYST = 10540;
 	public const ANCIENT_DEBRIS = 10541;
@@ -737,8 +737,16 @@ private function __construct(){
 	public const BIG_DRIPLEAF_HEAD = 10707;
 	public const BIG_DRIPLEAF_STEM = 10708;
 	public const PINK_PETALS = 10709;
+	public const CRIMSON_ROOTS = 10710;
+	public const WARPED_ROOTS = 10711;
+	public const CHISELED_BOOKSHELF = 10712;
+	public const TORCHFLOWER = 10713;
+	public const TORCHFLOWER_CROP = 10714;
+	public const PITCHER_PLANT = 10715;
+	public const PITCHER_CROP = 10716;
+	public const DOUBLE_PITCHER_CROP = 10717;
 
-	public const FIRST_UNUSED_BLOCK_ID = 10710;
+	public const FIRST_UNUSED_BLOCK_ID = 10718;
 
 	private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID;
 
diff --git a/src/block/BrewingStand.php b/src/block/BrewingStand.php
index 497d282d750..03969316494 100644
--- a/src/block/BrewingStand.php
+++ b/src/block/BrewingStand.php
@@ -34,6 +34,7 @@
 use pocketmine\math\Vector3;
 use pocketmine\player\Player;
 use function array_key_exists;
+use function spl_object_id;
 
 class BrewingStand extends Transparent{
 
@@ -44,7 +45,7 @@ class BrewingStand extends Transparent{
 	protected array $slots = [];
 
 	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
-		$w->brewingStandSlots($this->slots);
+		$w->enumSet($this->slots, BrewingStandSlot::cases());
 	}
 
 	protected function recalculateCollisionBoxes() : array{
@@ -61,18 +62,18 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	public function hasSlot(BrewingStandSlot $slot) : bool{
-		return array_key_exists($slot->id(), $this->slots);
+		return array_key_exists(spl_object_id($slot), $this->slots);
 	}
 
 	public function setSlot(BrewingStandSlot $slot, bool $occupied) : self{
 		if($occupied){
-			$this->slots[$slot->id()] = $slot;
+			$this->slots[spl_object_id($slot)] = $slot;
 		}else{
-			unset($this->slots[$slot->id()]);
+			unset($this->slots[spl_object_id($slot)]);
 		}
 		return $this;
 	}
@@ -89,7 +90,7 @@ public function getSlots() : array{
 	public function setSlots(array $slots) : self{
 		$this->slots = [];
 		foreach($slots as $slot){
-			$this->slots[$slot->id()] = $slot;
+			$this->slots[spl_object_id($slot)] = $slot;
 		}
 		return $this;
 	}
@@ -114,7 +115,7 @@ public function onScheduledUpdate() : void{
 			}
 
 			$changed = false;
-			foreach(BrewingStandSlot::getAll() as $slot){
+			foreach(BrewingStandSlot::cases() as $slot){
 				$occupied = !$brewing->getInventory()->isSlotEmpty($slot->getSlotNumber());
 				if($occupied !== $this->hasSlot($slot)){
 					$this->setSlot($slot, $occupied);
diff --git a/src/block/BuddingAmethyst.php b/src/block/BuddingAmethyst.php
new file mode 100644
index 00000000000..215a038f98e
--- /dev/null
+++ b/src/block/BuddingAmethyst.php
@@ -0,0 +1,68 @@
+<?php
+
+/*
+ *
+ *  ____            _        _   __  __ _                  __  __ ____
+ * |  _ \ ___   ___| | _____| |_|  \/  (_)_ __   ___      |  \/  |  _ \
+ * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
+ * |  __/ (_) | (__|   <  __/ |_| |  | | | | | |  __/_____| |  | |  __/
+ * |_|   \___/ \___|_|\_\___|\__|_|  |_|_|_| |_|\___|     |_|  |_|_|
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * @author PocketMine Team
+ * @link http://www.pocketmine.net/
+ *
+ *
+ */
+
+declare(strict_types=1);
+
+namespace pocketmine\block;
+
+use pocketmine\block\utils\AmethystTrait;
+use pocketmine\block\utils\BlockEventHelper;
+use pocketmine\item\Item;
+use pocketmine\math\Facing;
+use function array_rand;
+use function mt_rand;
+
+final class BuddingAmethyst extends Opaque{
+	use AmethystTrait;
+
+	public function ticksRandomly() : bool{
+		return true;
+	}
+
+	public function onRandomTick() : void{
+		if(mt_rand(1, 5) === 1){
+			$face = Facing::ALL[array_rand(Facing::ALL)];
+
+			$adjacent = $this->getSide($face);
+			//TODO: amethyst buds can spawn in water - we need waterlogging support for this
+
+			$newStage = null;
+
+			if($adjacent->getTypeId() === BlockTypeIds::AIR){
+				$newStage = AmethystCluster::STAGE_SMALL_BUD;
+			}elseif(
+				$adjacent->getTypeId() === BlockTypeIds::AMETHYST_CLUSTER &&
+				$adjacent instanceof AmethystCluster &&
+				$adjacent->getStage() < AmethystCluster::STAGE_CLUSTER &&
+				$adjacent->getFacing() === $face
+			){
+				$newStage = $adjacent->getStage() + 1;
+			}
+			if($newStage !== null){
+				BlockEventHelper::grow($adjacent, VanillaBlocks::AMETHYST_CLUSTER()->setStage($newStage)->setFacing($face), null);
+			}
+		}
+	}
+
+	public function getDropsForCompatibleTool(Item $item) : array{
+		return [];
+	}
+}
diff --git a/src/block/Cactus.php b/src/block/Cactus.php
index bb8a7cd40ab..6f2b04c8a38 100644
--- a/src/block/Cactus.php
+++ b/src/block/Cactus.php
@@ -23,38 +23,21 @@
 
 namespace pocketmine\block;
 
+use pocketmine\block\utils\AgeableTrait;
 use pocketmine\block\utils\BlockEventHelper;
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\block\utils\SupportType;
-use pocketmine\data\runtime\RuntimeDataDescriber;
 use pocketmine\entity\Entity;
 use pocketmine\event\entity\EntityDamageByBlockEvent;
 use pocketmine\event\entity\EntityDamageEvent;
-use pocketmine\item\Item;
 use pocketmine\math\AxisAlignedBB;
 use pocketmine\math\Facing;
-use pocketmine\math\Vector3;
-use pocketmine\player\Player;
-use pocketmine\world\BlockTransaction;
 
 class Cactus extends Transparent{
-	public const MAX_AGE = 15;
-
-	protected int $age = 0;
-
-	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
-		$w->boundedInt(4, 0, self::MAX_AGE, $this->age);
-	}
-
-	public function getAge() : int{ return $this->age; }
+	use AgeableTrait;
+	use StaticSupportTrait;
 
-	/** @return $this */
-	public function setAge(int $age) : self{
-		if($age < 0 || $age > self::MAX_AGE){
-			throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
-		}
-		$this->age = $age;
-		return $this;
-	}
+	public const MAX_AGE = 15;
 
 	public function hasEntityCollision() : bool{
 		return true;
@@ -69,7 +52,7 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	public function onEntityInside(Entity $entity) : bool{
@@ -78,23 +61,18 @@ public function onEntityInside(Entity $entity) : bool{
 		return true;
 	}
 
-	private function canBeSupportedBy(Block $block) : bool{
-		return $block->hasSameTypeId($this) || $block->hasTypeTag(BlockTypeTags::SAND);
-	}
-
-	public function onNearbyBlockChange() : void{
-		$world = $this->position->getWorld();
-		if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
-			$world->useBreakOn($this->position);
-		}else{
-			foreach(Facing::HORIZONTAL as $side){
-				$b = $this->getSide($side);
-				if($b->isSolid()){
-					$world->useBreakOn($this->position);
-					break;
-				}
+	private function canBeSupportedAt(Block $block) : bool{
+		$supportBlock = $block->getSide(Facing::DOWN);
+		if(!$supportBlock->hasSameTypeId($this) && !$supportBlock->hasTypeTag(BlockTypeTags::SAND)){
+			return false;
+		}
+		foreach(Facing::HORIZONTAL as $side){
+			if($block->getSide($side)->isSolid()){
+				return false;
 			}
 		}
+
+		return true;
 	}
 
 	public function ticksRandomly() : bool{
@@ -124,18 +102,4 @@ public function onRandomTick() : void{
 			}
 		}
 	}
-
-	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		if($this->canBeSupportedBy($this->getSide(Facing::DOWN))){
-			foreach(Facing::HORIZONTAL as $side){
-				if($this->getSide($side)->isSolid()){
-					return false;
-				}
-			}
-
-			return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
-		}
-
-		return false;
-	}
 }
diff --git a/src/block/Cake.php b/src/block/Cake.php
index 5d4e3cfcae6..073fc62ac90 100644
--- a/src/block/Cake.php
+++ b/src/block/Cake.php
@@ -37,7 +37,7 @@ class Cake extends BaseCake{
 	protected int $bites = 0;
 
 	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
-		$w->boundedInt(3, 0, self::MAX_BITES, $this->bites);
+		$w->boundedIntAuto(0, self::MAX_BITES, $this->bites);
 	}
 
 	/**
diff --git a/src/block/CakeWithDyedCandle.php b/src/block/CakeWithDyedCandle.php
index e01ad3e1b97..0dff358e164 100644
--- a/src/block/CakeWithDyedCandle.php
+++ b/src/block/CakeWithDyedCandle.php
@@ -30,7 +30,7 @@ class CakeWithDyedCandle extends CakeWithCandle{
 	use ColoredTrait;
 
 	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->color = DyeColor::WHITE();
+		$this->color = DyeColor::WHITE;
 		parent::__construct($idInfo, $name, $typeInfo);
 	}
 
diff --git a/src/block/Candle.php b/src/block/Candle.php
index 7009acef618..7f22641e118 100644
--- a/src/block/Candle.php
+++ b/src/block/Candle.php
@@ -48,7 +48,7 @@ class Candle extends Transparent{
 
 	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
 		$this->encodeLitState($w);
-		$w->boundedInt(2, self::MIN_COUNT, self::MAX_COUNT, $this->count);
+		$w->boundedIntAuto(self::MIN_COUNT, self::MAX_COUNT, $this->count);
 	}
 
 	public function getCount() : int{ return $this->count; }
@@ -91,7 +91,7 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	protected function getCandleIfCompatibleType(Block $block) : ?Candle{
diff --git a/src/block/Carpet.php b/src/block/Carpet.php
index c979571ebfa..1ee7240c516 100644
--- a/src/block/Carpet.php
+++ b/src/block/Carpet.php
@@ -24,21 +24,13 @@
 namespace pocketmine\block;
 
 use pocketmine\block\utils\ColoredTrait;
-use pocketmine\block\utils\DyeColor;
-use pocketmine\item\Item;
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\math\AxisAlignedBB;
 use pocketmine\math\Facing;
-use pocketmine\math\Vector3;
-use pocketmine\player\Player;
-use pocketmine\world\BlockTransaction;
 
 class Carpet extends Flowable{
 	use ColoredTrait;
-
-	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->color = DyeColor::WHITE();
-		parent::__construct($idInfo, $name, $typeInfo);
-	}
+	use StaticSupportTrait;
 
 	public function isSolid() : bool{
 		return true;
@@ -51,19 +43,8 @@ protected function recalculateCollisionBoxes() : array{
 		return [AxisAlignedBB::one()->trim(Facing::UP, 15 / 16)];
 	}
 
-	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		$down = $this->getSide(Facing::DOWN);
-		if($down->getTypeId() !== BlockTypeIds::AIR){
-			return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
-		}
-
-		return false;
-	}
-
-	public function onNearbyBlockChange() : void{
-		if($this->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::AIR){
-			$this->position->getWorld()->useBreakOn($this->position);
-		}
+	private function canBeSupportedAt(Block $block) : bool{
+		return $block->getSide(Facing::DOWN)->getTypeId() !== BlockTypeIds::AIR;
 	}
 
 	public function getFlameEncouragement() : int{
diff --git a/src/block/Cauldron.php b/src/block/Cauldron.php
index da1a938b297..772583a5af9 100644
--- a/src/block/Cauldron.php
+++ b/src/block/Cauldron.php
@@ -61,7 +61,7 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return $facing === Facing::UP ? SupportType::EDGE() : SupportType::NONE();
+		return $facing === Facing::UP ? SupportType::EDGE : SupportType::NONE;
 	}
 
 	/**
@@ -83,7 +83,7 @@ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player
 		}elseif($item->getTypeId() === ItemTypeIds::POWDER_SNOW_BUCKET){
 			//TODO: powder snow cauldron
 		}elseif($item instanceof Potion || $item instanceof SplashPotion){ //TODO: lingering potion
-			if($item->getType()->equals(PotionType::WATER())){
+			if($item->getType() === PotionType::WATER){
 				$this->fill(WaterCauldron::WATER_BOTTLE_FILL_AMOUNT, VanillaBlocks::WATER_CAULDRON(), $item, VanillaItems::GLASS_BOTTLE(), $returnedItems);
 			}else{
 				$this->fill(PotionCauldron::POTION_FILL_AMOUNT, VanillaBlocks::POTION_CAULDRON()->setPotionItem($item), $item, VanillaItems::GLASS_BOTTLE(), $returnedItems);
diff --git a/src/block/CaveVines.php b/src/block/CaveVines.php
index ce8fdd9708f..daa973507f3 100644
--- a/src/block/CaveVines.php
+++ b/src/block/CaveVines.php
@@ -23,7 +23,9 @@
 
 namespace pocketmine\block;
 
+use pocketmine\block\utils\AgeableTrait;
 use pocketmine\block\utils\BlockEventHelper;
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\block\utils\SupportType;
 use pocketmine\data\runtime\RuntimeDataDescriber;
 use pocketmine\entity\Entity;
@@ -38,14 +40,16 @@
 use function mt_rand;
 
 class CaveVines extends Flowable{
+	use AgeableTrait;
+	use StaticSupportTrait;
+
 	public const MAX_AGE = 25;
 
-	protected int $age = 0;
 	protected bool $berries = false;
 	protected bool $head = false;
 
 	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
-		$w->boundedInt(5, 0, self::MAX_AGE, $this->age);
+		$w->boundedIntAuto(0, self::MAX_AGE, $this->age);
 		$w->bool($this->berries);
 		$w->bool($this->head);
 	}
@@ -66,19 +70,6 @@ public function setHead(bool $head) : self{
 		return $this;
 	}
 
-	public function getAge() : int{
-		return $this->age;
-	}
-
-	/** @return $this */
-	public function setAge(int $age) : self{
-		if($age < 0 || $age > self::MAX_AGE){
-			throw new \InvalidArgumentException("Age must be in range 0-" . self::MAX_AGE);
-		}
-		$this->age = $age;
-		return $this;
-	}
-
 	public function canClimb() : bool{
 		return true;
 	}
@@ -89,19 +80,10 @@ public function getLightLevel() : int{
 
 	private function canBeSupportedAt(Block $block) : bool{
 		$supportBlock = $block->getSide(Facing::UP);
-		return $supportBlock->getSupportType(Facing::DOWN)->equals(SupportType::FULL()) || $supportBlock->hasSameTypeId($this);
-	}
-
-	public function onNearbyBlockChange() : void{
-		if(!$this->canBeSupportedAt($this)){
-			$this->position->getWorld()->useBreakOn($this->position);
-		}
+		return $supportBlock->getSupportType(Facing::DOWN) === SupportType::FULL || $supportBlock->hasSameTypeId($this);
 	}
 
 	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		if(!$this->canBeSupportedAt($blockReplace)){
-			return false;
-		}
 		$this->age = mt_rand(0, self::MAX_AGE);
 		return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
 	}
@@ -177,6 +159,6 @@ public function asItem() : Item{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 }
diff --git a/src/block/Chain.php b/src/block/Chain.php
index fa8ffd1e822..e9cc2c9be8d 100644
--- a/src/block/Chain.php
+++ b/src/block/Chain.php
@@ -33,7 +33,7 @@ final class Chain extends Transparent{
 	use PillarRotationTrait;
 
 	public function getSupportType(int $facing) : SupportType{
-		return $this->axis === Axis::Y && Facing::axis($facing) === Axis::Y ? SupportType::CENTER() : SupportType::NONE();
+		return $this->axis === Axis::Y && Facing::axis($facing) === Axis::Y ? SupportType::CENTER : SupportType::NONE;
 	}
 
 	protected function recalculateCollisionBoxes() : array{
diff --git a/src/block/Chest.php b/src/block/Chest.php
index 270c696c33a..dca21576aa9 100644
--- a/src/block/Chest.php
+++ b/src/block/Chest.php
@@ -45,7 +45,7 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	public function onPostPlace() : void{
diff --git a/src/block/ChiseledBookshelf.php b/src/block/ChiseledBookshelf.php
new file mode 100644
index 00000000000..021ed58e993
--- /dev/null
+++ b/src/block/ChiseledBookshelf.php
@@ -0,0 +1,134 @@
+<?php
+
+/*
+ *
+ *  ____            _        _   __  __ _                  __  __ ____
+ * |  _ \ ___   ___| | _____| |_|  \/  (_)_ __   ___      |  \/  |  _ \
+ * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
+ * |  __/ (_) | (__|   <  __/ |_| |  | | | | | |  __/_____| |  | |  __/
+ * |_|   \___/ \___|_|\_\___|\__|_|  |_|_|_| |_|\___|     |_|  |_|_|
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * @author PocketMine Team
+ * @link http://www.pocketmine.net/
+ *
+ *
+ */
+
+declare(strict_types=1);
+
+namespace pocketmine\block;
+
+use pocketmine\block\tile\ChiseledBookshelf as TileChiseledBookshelf;
+use pocketmine\block\utils\ChiseledBookshelfSlot;
+use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
+use pocketmine\block\utils\HorizontalFacingTrait;
+use pocketmine\data\runtime\RuntimeDataDescriber;
+use pocketmine\item\Book;
+use pocketmine\item\EnchantedBook;
+use pocketmine\item\Item;
+use pocketmine\item\WritableBookBase;
+use pocketmine\math\Axis;
+use pocketmine\math\Facing;
+use pocketmine\math\Vector3;
+use pocketmine\player\Player;
+use function spl_object_id;
+
+class ChiseledBookshelf extends Opaque{
+	use HorizontalFacingTrait;
+	use FacesOppositePlacingPlayerTrait;
+
+	/**
+	 * @var ChiseledBookshelfSlot[]
+	 * @phpstan-var array<int, ChiseledBookshelfSlot>
+	 */
+	private array $slots = [];
+
+	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
+		$w->horizontalFacing($this->facing);
+		$w->enumSet($this->slots, ChiseledBookshelfSlot::cases());
+	}
+
+	/**
+	 * Returns whether the given slot is displayed as occupied.
+	 * This doesn't guarantee that there is or isn't a book in the bookshelf's inventory.
+	 */
+	public function hasSlot(ChiseledBookshelfSlot $slot) : bool{
+		return isset($this->slots[spl_object_id($slot)]);
+	}
+
+	/**
+	 * Sets whether the given slot is displayed as occupied.
+	 *
+	 * This doesn't modify the bookshelf's inventory, so you can use this to make invisible
+	 * books or display books that aren't actually in the bookshelf.
+	 *
+	 * To modify the contents of the bookshelf inventory, access the tile inventory.
+	 *
+	 * @return $this
+	 */
+	public function setSlot(ChiseledBookshelfSlot $slot, bool $occupied) : self{
+		if($occupied){
+			$this->slots[spl_object_id($slot)] = $slot;
+		}else{
+			unset($this->slots[spl_object_id($slot)]);
+		}
+		return $this;
+	}
+
+	/**
+	 * Returns which slots of the bookshelf are displayed as occupied.
+	 * As above, these values do not necessarily reflect the contents of the bookshelf inventory,
+	 * although they usually will unless modified by plugins.
+	 *
+	 * @return ChiseledBookshelfSlot[]
+	 * @phpstan-return array<int, ChiseledBookshelfSlot>
+	 */
+	public function getSlots() : array{
+		return $this->slots;
+	}
+
+	public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
+		if($face !== $this->facing){
+			return false;
+		}
+
+		$x = Facing::axis($face) === Axis::X ? $clickVector->getZ() : $clickVector->getX();
+		$slot = ChiseledBookshelfSlot::fromBlockFaceCoordinates(
+			Facing::isPositive(Facing::rotateY($face, true)) ? 1 - $x : $x,
+			$clickVector->y
+		);
+		$tile = $this->position->getWorld()->getTile($this->position);
+		if(!$tile instanceof TileChiseledBookshelf){
+			return false;
+		}
+
+		$inventory = $tile->getInventory();
+		if(!$inventory->isSlotEmpty($slot->value)){
+			$returnedItems[] = $inventory->getItem($slot->value);
+			$inventory->clear($slot->value);
+			$this->setSlot($slot, false);
+		}elseif($item instanceof WritableBookBase || $item instanceof Book || $item instanceof EnchantedBook){
+			//TODO: type tags like blocks would be better for this
+			$inventory->setItem($slot->value, $item->pop());
+			$this->setSlot($slot, true);
+		}else{
+			return true;
+		}
+
+		$this->position->getWorld()->setBlock($this->position, $this);
+		return true;
+	}
+
+	public function getDropsForCompatibleTool(Item $item) : array{
+		return [];
+	}
+
+	public function isAffectedBySilkTouch() : bool{
+		return true;
+	}
+}
diff --git a/src/block/ChorusFlower.php b/src/block/ChorusFlower.php
index 5c5077f22ac..2dcf4bb7019 100644
--- a/src/block/ChorusFlower.php
+++ b/src/block/ChorusFlower.php
@@ -23,52 +23,38 @@
 
 namespace pocketmine\block;
 
-use pocketmine\data\runtime\RuntimeDataDescriber;
+use pocketmine\block\utils\AgeableTrait;
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\entity\projectile\Projectile;
 use pocketmine\event\block\StructureGrowEvent;
-use pocketmine\item\Item;
 use pocketmine\math\Axis;
 use pocketmine\math\AxisAlignedBB;
 use pocketmine\math\Facing;
 use pocketmine\math\RayTraceResult;
 use pocketmine\math\Vector3;
-use pocketmine\player\Player;
 use pocketmine\world\BlockTransaction;
-use pocketmine\world\Position;
 use pocketmine\world\sound\ChorusFlowerDieSound;
 use pocketmine\world\sound\ChorusFlowerGrowSound;
 use pocketmine\world\World;
 use function array_rand;
+use function min;
 use function mt_rand;
 
 final class ChorusFlower extends Flowable{
+	use AgeableTrait;
+	use StaticSupportTrait;
+
 	public const MIN_AGE = 0;
 	public const MAX_AGE = 5;
 
 	private const MAX_STEM_HEIGHT = 5;
 
-	private int $age = self::MIN_AGE;
-
-	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
-		$w->boundedInt(3, self::MIN_AGE, self::MAX_AGE, $this->age);
-	}
-
-	public function getAge() : int{ return $this->age; }
-
-	/** @return $this */
-	public function setAge(int $age) : self{
-		if($age < self::MIN_AGE || $age > self::MAX_AGE){
-			throw new \InvalidArgumentException("Age must be in the range " . self::MIN_AGE . " ... " . self::MAX_AGE);
-		}
-		$this->age = $age;
-		return $this;
-	}
-
 	protected function recalculateCollisionBoxes() : array{
 		return [AxisAlignedBB::one()];
 	}
 
-	private function canBeSupportedAt(Position $position) : bool{
+	private function canBeSupportedAt(Block $block) : bool{
+		$position = $block->getPosition();
 		$world = $position->getWorld();
 		$down = $world->getBlock($position->down());
 
@@ -93,25 +79,10 @@ private function canBeSupportedAt(Position $position) : bool{
 		return $plantAdjacent;
 	}
 
-	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		if(!$this->canBeSupportedAt($blockReplace->getPosition())){
-			return false;
-		}
-		return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
-	}
-
-	public function onNearbyBlockChange() : void{
-		if(!$this->canBeSupportedAt($this->position)){
-			$this->position->getWorld()->useBreakOn($this->position);
-		}
-	}
-
 	public function onProjectileHit(Projectile $projectile, RayTraceResult $hitResult) : void{
 		$this->position->getWorld()->useBreakOn($this->position);
 	}
 
-	public function ticksRandomly() : bool{ return $this->age < self::MAX_AGE; }
-
 	/**
 	 * @phpstan-return array{int, bool}
 	 */
@@ -181,11 +152,13 @@ private function grow(int $facing, int $ageChange, ?BlockTransaction $tx) : Bloc
 		if($tx === null){
 			$tx = new BlockTransaction($this->position->getWorld());
 		}
-		$tx->addBlock($this->position->getSide($facing), (clone $this)->setAge($this->getAge() + $ageChange));
+		$tx->addBlock($this->position->getSide($facing), (clone $this)->setAge(min(self::MAX_AGE, $this->getAge() + $ageChange)));
 
 		return $tx;
 	}
 
+	public function ticksRandomly() : bool{ return $this->age < self::MAX_AGE; }
+
 	public function onRandomTick() : void{
 		$world = $this->position->getWorld();
 
diff --git a/src/block/ChorusPlant.php b/src/block/ChorusPlant.php
index f7642bd0480..e3cc8de9d00 100644
--- a/src/block/ChorusPlant.php
+++ b/src/block/ChorusPlant.php
@@ -23,18 +23,16 @@
 
 namespace pocketmine\block;
 
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\item\Item;
 use pocketmine\item\VanillaItems;
 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\Position;
 use function mt_rand;
 
 final class ChorusPlant extends Flowable{
+	use StaticSupportTrait;
 
 	protected function recalculateCollisionBoxes() : array{
 		$bb = AxisAlignedBB::one();
@@ -52,7 +50,8 @@ private function canBeSupportedBy(Block $block) : bool{
 		return $block->hasSameTypeId($this) || $block->getTypeId() === BlockTypeIds::END_STONE;
 	}
 
-	private function canStay(Position $position) : bool{
+	private function canBeSupportedAt(Block $block) : bool{
+		$position = $block->getPosition();
 		$world = $position->getWorld();
 
 		$down = $world->getBlock($position->down());
@@ -72,24 +71,7 @@ private function canStay(Position $position) : bool{
 			}
 		}
 
-		if($this->canBeSupportedBy($down)){
-			return true;
-		}
-
-		return false;
-	}
-
-	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		if(!$this->canStay($blockReplace->getPosition())){
-			return false;
-		}
-		return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
-	}
-
-	public function onNearbyBlockChange() : void{
-		if(!$this->canStay($this->position)){
-			$this->position->getWorld()->useBreakOn($this->position);
-		}
+		return $this->canBeSupportedBy($down);
 	}
 
 	public function getDropsForCompatibleTool(Item $item) : array{
diff --git a/src/block/CocoaBlock.php b/src/block/CocoaBlock.php
index 69f94eaf6f9..5cec4b933a2 100644
--- a/src/block/CocoaBlock.php
+++ b/src/block/CocoaBlock.php
@@ -23,6 +23,7 @@
 
 namespace pocketmine\block;
 
+use pocketmine\block\utils\AgeableTrait;
 use pocketmine\block\utils\BlockEventHelper;
 use pocketmine\block\utils\HorizontalFacingTrait;
 use pocketmine\block\utils\SupportType;
@@ -41,25 +42,13 @@
 
 class CocoaBlock extends Transparent{
 	use HorizontalFacingTrait;
+	use AgeableTrait;
 
 	public const MAX_AGE = 2;
 
-	protected int $age = 0;
-
 	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
 		$w->horizontalFacing($this->facing);
-		$w->boundedInt(2, 0, self::MAX_AGE, $this->age);
-	}
-
-	public function getAge() : int{ return $this->age; }
-
-	/** @return $this */
-	public function setAge(int $age) : self{
-		if($age < 0 || $age > self::MAX_AGE){
-			throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
-		}
-		$this->age = $age;
-		return $this;
+		$w->boundedIntAuto(0, self::MAX_AGE, $this->age);
 	}
 
 	/**
@@ -77,11 +66,11 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	private function canAttachTo(Block $block) : bool{
-		return $block instanceof Wood && $block->getWoodType()->equals(WoodType::JUNGLE());
+		return $block instanceof Wood && $block->getWoodType() === WoodType::JUNGLE;
 	}
 
 	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
@@ -110,7 +99,7 @@ public function onNearbyBlockChange() : void{
 	}
 
 	public function ticksRandomly() : bool{
-		return true;
+		return $this->age < self::MAX_AGE;
 	}
 
 	public function onRandomTick() : void{
diff --git a/src/block/Concrete.php b/src/block/Concrete.php
index cb8ee3b5295..fae6f8e2f82 100644
--- a/src/block/Concrete.php
+++ b/src/block/Concrete.php
@@ -24,13 +24,7 @@
 namespace pocketmine\block;
 
 use pocketmine\block\utils\ColoredTrait;
-use pocketmine\block\utils\DyeColor;
 
 class Concrete extends Opaque{
 	use ColoredTrait;
-
-	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->color = DyeColor::WHITE();
-		parent::__construct($idInfo, $name, $typeInfo);
-	}
 }
diff --git a/src/block/ConcretePowder.php b/src/block/ConcretePowder.php
index 635d3ff2cf2..59f14bc7226 100644
--- a/src/block/ConcretePowder.php
+++ b/src/block/ConcretePowder.php
@@ -25,7 +25,6 @@
 
 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\math\Facing;
@@ -36,11 +35,6 @@ class ConcretePowder extends Opaque implements Fallable{
 		onNearbyBlockChange as protected startFalling;
 	}
 
-	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->color = DyeColor::WHITE();
-		parent::__construct($idInfo, $name, $typeInfo);
-	}
-
 	public function onNearbyBlockChange() : void{
 		if(($water = $this->getAdjacentWater()) !== null){
 			BlockEventHelper::form($this, VanillaBlocks::CONCRETE()->setColor($this->color), $water);
diff --git a/src/block/CopperStairs.php b/src/block/CopperStairs.php
index fe52616ce90..b16d49ec1ee 100644
--- a/src/block/CopperStairs.php
+++ b/src/block/CopperStairs.php
@@ -23,14 +23,8 @@
 
 namespace pocketmine\block;
 
-use pocketmine\block\utils\CopperOxidation;
 use pocketmine\block\utils\CopperTrait;
 
 class CopperStairs extends Stair{
 	use CopperTrait;
-
-	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->oxidation = CopperOxidation::NONE();
-		parent::__construct($idInfo, $name, $typeInfo);
-	}
 }
diff --git a/src/block/Coral.php b/src/block/Coral.php
index 837a8185743..96c6d4fe0ee 100644
--- a/src/block/Coral.php
+++ b/src/block/Coral.php
@@ -23,29 +23,11 @@
 
 namespace pocketmine\block;
 
-use pocketmine\item\Item;
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\math\Facing;
-use pocketmine\math\Vector3;
-use pocketmine\player\Player;
-use pocketmine\world\BlockTransaction;
 
 final class Coral extends BaseCoral{
-
-	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		if(!$this->canBeSupportedAt($blockReplace)){
-			return false;
-		}
-		return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
-	}
-
-	public function onNearbyBlockChange() : void{
-		$world = $this->position->getWorld();
-		if(!$this->canBeSupportedAt($this)){
-			$world->useBreakOn($this->position);
-		}else{
-			parent::onNearbyBlockChange();
-		}
-	}
+	use StaticSupportTrait;
 
 	private function canBeSupportedAt(Block $block) : bool{
 		return $block->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport();
diff --git a/src/block/CoralBlock.php b/src/block/CoralBlock.php
index 5ce58f413d7..3e7ca8224f7 100644
--- a/src/block/CoralBlock.php
+++ b/src/block/CoralBlock.php
@@ -24,7 +24,6 @@
 namespace pocketmine\block;
 
 use pocketmine\block\utils\BlockEventHelper;
-use pocketmine\block\utils\CoralType;
 use pocketmine\block\utils\CoralTypeTrait;
 use pocketmine\item\Item;
 use function mt_rand;
@@ -32,11 +31,6 @@
 final class CoralBlock extends Opaque{
 	use CoralTypeTrait;
 
-	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->coralType = CoralType::TUBE();
-		parent::__construct($idInfo, $name, $typeInfo);
-	}
-
 	public function onNearbyBlockChange() : void{
 		if(!$this->dead){
 			$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(40, 200));
diff --git a/src/block/Crops.php b/src/block/Crops.php
index d6e84c4243e..350268863d4 100644
--- a/src/block/Crops.php
+++ b/src/block/Crops.php
@@ -23,51 +23,34 @@
 
 namespace pocketmine\block;
 
+use pocketmine\block\utils\AgeableTrait;
 use pocketmine\block\utils\BlockEventHelper;
-use pocketmine\data\runtime\RuntimeDataDescriber;
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\item\Fertilizer;
 use pocketmine\item\Item;
 use pocketmine\math\Facing;
 use pocketmine\math\Vector3;
 use pocketmine\player\Player;
-use pocketmine\world\BlockTransaction;
 use function mt_rand;
 
 abstract class Crops extends Flowable{
-	public const MAX_AGE = 7;
-
-	protected int $age = 0;
-
-	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
-		$w->boundedInt(3, 0, self::MAX_AGE, $this->age);
-	}
-
-	public function getAge() : int{ return $this->age; }
-
-	/** @return $this */
-	public function setAge(int $age) : self{
-		if($age < 0 || $age > self::MAX_AGE){
-			throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
-		}
-		$this->age = $age;
-		return $this;
-	}
+	use AgeableTrait;
+	use StaticSupportTrait;
 
-	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		if($blockReplace->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::FARMLAND){
-			return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
-		}
+	public const MAX_AGE = 7;
 
-		return false;
+	private function canBeSupportedAt(Block $block) : bool{
+		return $block->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::FARMLAND;
 	}
 
 	public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
 		if($this->age < self::MAX_AGE && $item instanceof Fertilizer){
 			$block = clone $this;
-			$block->age += mt_rand(2, 5);
-			if($block->age > self::MAX_AGE){
-				$block->age = self::MAX_AGE;
+			$tempAge = $block->age + mt_rand(2, 5);
+			if($tempAge > self::MAX_AGE){
+				$tempAge = self::MAX_AGE;
 			}
+			$block->age = $tempAge;
 			if(BlockEventHelper::grow($this, $block, $player)){
 				$item->pop();
 			}
@@ -78,14 +61,8 @@ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player
 		return false;
 	}
 
-	public function onNearbyBlockChange() : void{
-		if($this->getSide(Facing::DOWN)->getTypeId() !== BlockTypeIds::FARMLAND){
-			$this->position->getWorld()->useBreakOn($this->position);
-		}
-	}
-
 	public function ticksRandomly() : bool{
-		return true;
+		return $this->age < self::MAX_AGE;
 	}
 
 	public function onRandomTick() : void{
diff --git a/src/block/DaylightSensor.php b/src/block/DaylightSensor.php
index e1f78aef5b9..4141a2b7eda 100644
--- a/src/block/DaylightSensor.php
+++ b/src/block/DaylightSensor.php
@@ -42,7 +42,7 @@ class DaylightSensor extends Transparent{
 	protected bool $inverted = false;
 
 	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
-		$w->boundedInt(4, 0, 15, $this->signalStrength);
+		$w->boundedIntAuto(0, 15, $this->signalStrength);
 		$w->bool($this->inverted);
 	}
 
@@ -70,7 +70,7 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
diff --git a/src/block/DeadBush.php b/src/block/DeadBush.php
index b265e76117e..0c089a6f382 100644
--- a/src/block/DeadBush.php
+++ b/src/block/DeadBush.php
@@ -23,29 +23,14 @@
 
 namespace pocketmine\block;
 
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\item\Item;
 use pocketmine\item\VanillaItems;
 use pocketmine\math\Facing;
-use pocketmine\math\Vector3;
-use pocketmine\player\Player;
-use pocketmine\world\BlockTransaction;
 use function mt_rand;
 
 class DeadBush extends Flowable{
-
-	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		if($this->canBeSupportedBy($this->getSide(Facing::DOWN))){
-			return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
-		}
-
-		return false;
-	}
-
-	public function onNearbyBlockChange() : void{
-		if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
-			$this->position->getWorld()->useBreakOn($this->position);
-		}
-	}
+	use StaticSupportTrait;
 
 	public function getDropsForIncompatibleTool(Item $item) : array{
 		return [
@@ -65,11 +50,12 @@ public function getFlammability() : int{
 		return 100;
 	}
 
-	private function canBeSupportedBy(Block $block) : bool{
+	private function canBeSupportedAt(Block $block) : bool{
+		$supportBlock = $block->getSide(Facing::DOWN);
 		return
-			$block->hasTypeTag(BlockTypeTags::SAND) ||
-			$block->hasTypeTag(BlockTypeTags::MUD) ||
-			match($block->getTypeId()){
+			$supportBlock->hasTypeTag(BlockTypeTags::SAND) ||
+			$supportBlock->hasTypeTag(BlockTypeTags::MUD) ||
+			match($supportBlock->getTypeId()){
 				//can't use DIRT tag here because it includes farmland
 				BlockTypeIds::PODZOL,
 				BlockTypeIds::MYCELIUM,
diff --git a/src/block/Dirt.php b/src/block/Dirt.php
index 539454b4157..b3a3c6090bb 100644
--- a/src/block/Dirt.php
+++ b/src/block/Dirt.php
@@ -38,15 +38,10 @@
 use pocketmine\world\sound\WaterSplashSound;
 
 class Dirt extends Opaque{
-	protected DirtType $dirtType;
-
-	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->dirtType = DirtType::NORMAL();
-		parent::__construct($idInfo, $name, $typeInfo);
-	}
+	protected DirtType $dirtType = DirtType::NORMAL;
 
 	public function describeBlockItemState(RuntimeDataDescriber $w) : void{
-		$w->dirtType($this->dirtType);
+		$w->enum($this->dirtType);
 	}
 
 	public function getDirtType() : DirtType{ return $this->dirtType; }
@@ -62,16 +57,16 @@ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player
 		if($face === Facing::UP && $item instanceof Hoe){
 			$item->applyDamage(1);
 
-			$newBlock = $this->dirtType->equals(DirtType::NORMAL()) ? VanillaBlocks::FARMLAND() : VanillaBlocks::DIRT();
+			$newBlock = $this->dirtType === DirtType::NORMAL ? VanillaBlocks::FARMLAND() : VanillaBlocks::DIRT();
 			$center = $this->position->add(0.5, 0.5, 0.5);
 			$world->addSound($center, new ItemUseOnBlockSound($newBlock));
 			$world->setBlock($this->position, $newBlock);
-			if($this->dirtType->equals(DirtType::ROOTED())){
+			if($this->dirtType === DirtType::ROOTED){
 				$world->dropItem($center, VanillaBlocks::HANGING_ROOTS()->asItem());
 			}
 
 			return true;
-		}elseif($this->dirtType->equals(DirtType::ROOTED()) && $item instanceof Fertilizer){
+		}elseif($this->dirtType === DirtType::ROOTED && $item instanceof Fertilizer){
 			$down = $this->getSide(Facing::DOWN);
 			if($down->getTypeId() !== BlockTypeIds::AIR){
 				return true;
@@ -80,7 +75,7 @@ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player
 			$item->pop();
 			$world->setBlock($down->position, VanillaBlocks::HANGING_ROOTS());
 			//TODO: bonemeal particles, growth sounds
-		}elseif(($item instanceof Potion || $item instanceof SplashPotion) && $item->getType()->equals(PotionType::WATER())){
+		}elseif(($item instanceof Potion || $item instanceof SplashPotion) && $item->getType() === PotionType::WATER){
 			$item->pop();
 			$world->setBlock($this->position, VanillaBlocks::MUD());
 			$world->addSound($this->position, new WaterSplashSound(0.5));
diff --git a/src/block/Door.php b/src/block/Door.php
index a03427d5adb..c97c5bb1e9f 100644
--- a/src/block/Door.php
+++ b/src/block/Door.php
@@ -102,7 +102,7 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	public function onNearbyBlockChange() : void{
diff --git a/src/block/DoublePitcherCrop.php b/src/block/DoublePitcherCrop.php
new file mode 100644
index 00000000000..db700a625e4
--- /dev/null
+++ b/src/block/DoublePitcherCrop.php
@@ -0,0 +1,110 @@
+<?php
+
+/*
+ *
+ *  ____            _        _   __  __ _                  __  __ ____
+ * |  _ \ ___   ___| | _____| |_|  \/  (_)_ __   ___      |  \/  |  _ \
+ * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
+ * |  __/ (_) | (__|   <  __/ |_| |  | | | | | |  __/_____| |  | |  __/
+ * |_|   \___/ \___|_|\_\___|\__|_|  |_|_|_| |_|\___|     |_|  |_|_|
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * @author PocketMine Team
+ * @link http://www.pocketmine.net/
+ *
+ *
+ */
+
+declare(strict_types=1);
+
+namespace pocketmine\block;
+
+use pocketmine\block\utils\AgeableTrait;
+use pocketmine\data\runtime\RuntimeDataDescriber;
+use pocketmine\event\block\StructureGrowEvent;
+use pocketmine\item\Fertilizer;
+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 function mt_rand;
+
+final class DoublePitcherCrop extends DoublePlant{
+	use AgeableTrait {
+		describeBlockOnlyState as describeAge;
+	}
+
+	public const MAX_AGE = 1;
+
+	public function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
+		parent::describeBlockOnlyState($w);
+		$this->describeAge($w);
+	}
+
+	protected function recalculateCollisionBoxes() : array{
+		if($this->top){
+			return [];
+		}
+
+		//the pod exists only in the bottom half of the plant
+		return [
+			AxisAlignedBB::one()
+			->trim(Facing::UP, 11 / 16)
+			->squash(Axis::X, 3 / 16)
+			->squash(Axis::Z, 3 / 16)
+			->extend(Facing::DOWN, 1 / 16) //presumably this is to correct for farmland being 15/16 of a block tall
+		];
+	}
+
+	private function grow(?Player $player) : bool{
+		if($this->age >= self::MAX_AGE){
+			return false;
+		}
+
+		$bottom = $this->top ? $this->getSide(Facing::DOWN) : $this;
+		$top = $this->top ? $this : $this->getSide(Facing::UP);
+		if($top->getTypeId() !== BlockTypeIds::AIR && !$top->hasSameTypeId($this)){
+			return false;
+		}
+
+		$newState = (clone $this)->setAge($this->age + 1);
+
+		$tx = new BlockTransaction($this->position->getWorld());
+		$tx->addBlock($bottom->position, (clone $newState)->setTop(false));
+		$tx->addBlock($top->position, (clone $newState)->setTop(true));
+
+		$ev = new StructureGrowEvent($bottom, $tx, $player);
+		$ev->call();
+
+		return !$ev->isCancelled() && $tx->apply();
+
+	}
+
+	public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
+		if($item instanceof Fertilizer && $this->grow($player)){
+			$item->pop();
+			return true;
+		}
+
+		return false;
+	}
+
+	public function ticksRandomly() : bool{
+		return $this->age < self::MAX_AGE && !$this->top;
+	}
+
+	public function onRandomTick() : void{
+		//TODO: the growth speed is influenced by farmland and nearby crops
+		//only the bottom half of the plant can grow randomly
+		if(mt_rand(0, 2) === 0 && !$this->top){
+			$this->grow(null);
+		}
+	}
+}
diff --git a/src/block/DragonEgg.php b/src/block/DragonEgg.php
index 98809152d68..10fec639464 100644
--- a/src/block/DragonEgg.php
+++ b/src/block/DragonEgg.php
@@ -50,7 +50,7 @@ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player
 	}
 
 	public function onAttack(Item $item, int $face, ?Player $player = null) : bool{
-		if($player !== null && !$player->getGamemode()->equals(GameMode::CREATIVE())){
+		if($player !== null && $player->getGamemode() !== GameMode::CREATIVE){
 			$this->teleport();
 			return true;
 		}
@@ -82,6 +82,6 @@ public function teleport() : void{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 }
diff --git a/src/block/DyedCandle.php b/src/block/DyedCandle.php
index 55d76e4066a..a495e8d0033 100644
--- a/src/block/DyedCandle.php
+++ b/src/block/DyedCandle.php
@@ -24,19 +24,13 @@
 namespace pocketmine\block;
 
 use pocketmine\block\utils\ColoredTrait;
-use pocketmine\block\utils\DyeColor;
 
 class DyedCandle extends Candle{
 	use ColoredTrait;
 
-	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->color = DyeColor::WHITE();
-		parent::__construct($idInfo, $name, $typeInfo);
-	}
-
 	protected function getCandleIfCompatibleType(Block $block) : ?Candle{
 		$result = parent::getCandleIfCompatibleType($block);
 		//different coloured candles can't be combined in the same block
-		return $result instanceof DyedCandle && $result->color->equals($this->color) ? $result : null;
+		return $result instanceof DyedCandle && $result->color === $this->color ? $result : null;
 	}
 }
diff --git a/src/block/DyedShulkerBox.php b/src/block/DyedShulkerBox.php
index 196ee02822c..5eae9237e45 100644
--- a/src/block/DyedShulkerBox.php
+++ b/src/block/DyedShulkerBox.php
@@ -24,13 +24,7 @@
 namespace pocketmine\block;
 
 use pocketmine\block\utils\ColoredTrait;
-use pocketmine\block\utils\DyeColor;
 
 final class DyedShulkerBox extends ShulkerBox{
 	use ColoredTrait;
-
-	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->color = DyeColor::WHITE();
-		parent::__construct($idInfo, $name, $typeInfo);
-	}
 }
diff --git a/src/block/EnchantingTable.php b/src/block/EnchantingTable.php
index f80d52d124c..6a6c936b220 100644
--- a/src/block/EnchantingTable.php
+++ b/src/block/EnchantingTable.php
@@ -41,7 +41,7 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
diff --git a/src/block/EnderChest.php b/src/block/EnderChest.php
index 26596eac943..9004f7c79f9 100644
--- a/src/block/EnderChest.php
+++ b/src/block/EnderChest.php
@@ -49,7 +49,7 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
diff --git a/src/block/Farmland.php b/src/block/Farmland.php
index 2dd795eecc1..c2694dd8315 100644
--- a/src/block/Farmland.php
+++ b/src/block/Farmland.php
@@ -31,15 +31,40 @@
 use pocketmine\item\Item;
 use pocketmine\math\AxisAlignedBB;
 use pocketmine\math\Facing;
+use function intdiv;
 use function lcg_value;
 
 class Farmland extends Transparent{
 	public const MAX_WETNESS = 7;
 
+	private const WATER_SEARCH_HORIZONTAL_LENGTH = 9;
+
+	private const WATER_SEARCH_VERTICAL_LENGTH = 2;
+
+	private const WATER_POSITION_INDEX_UNKNOWN = -1;
+	/** Total possible options for water X/Z indexes */
+	private const WATER_POSITION_INDICES_TOTAL = (self::WATER_SEARCH_HORIZONTAL_LENGTH ** 2) * 2;
+
 	protected int $wetness = 0; //"moisture" blockstate property in PC
 
+	/**
+	 * Cached value indicating the relative coordinates of the most recently found water block.
+	 *
+	 * If this is set to a non-unknown value, the farmland block will check the relative coordinates indicated by
+	 * this value for water, before searching the entire 9x2x9 grid around the farmland. This significantly benefits
+	 * hydrating or fully hydrated farmland, avoiding the need for costly searches on every random tick.
+	 *
+	 * If the coordinates indicated don't contain water, the full 9x2x9 volume will be searched as before. A new index
+	 * will be recorded if water is found, otherwise it will be set to unknown and future searches will search the full
+	 * 9x2x9 volume again.
+	 *
+	 * This property is not exposed to the API or saved on disk. It is only used by PocketMine-MP at runtime as a cache.
+	 */
+	private int $waterPositionIndex = self::WATER_POSITION_INDEX_UNKNOWN;
+
 	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
-		$w->boundedInt(3, 0, self::MAX_WETNESS, $this->wetness);
+		$w->boundedIntAuto(0, self::MAX_WETNESS, $this->wetness);
+		$w->boundedIntAuto(-1, self::WATER_POSITION_INDICES_TOTAL - 1, $this->waterPositionIndex);
 	}
 
 	public function getWetness() : int{ return $this->wetness; }
@@ -53,6 +78,22 @@ public function setWetness(int $wetness) : self{
 		return $this;
 	}
 
+	/**
+	 * @internal
+	 */
+	public function getWaterPositionIndex() : int{ return $this->waterPositionIndex; }
+
+	/**
+	 * @internal
+	 */
+	public function setWaterPositionIndex(int $waterPositionIndex) : self{
+		if($waterPositionIndex < -1 || $waterPositionIndex >= self::WATER_POSITION_INDICES_TOTAL){
+			throw new \InvalidArgumentException("Water XZ index must be in range -1 ... " . (self::WATER_POSITION_INDICES_TOTAL - 1));
+		}
+		$this->waterPositionIndex = $waterPositionIndex;
+		return $this;
+	}
+
 	/**
 	 * @return AxisAlignedBB[]
 	 */
@@ -72,6 +113,11 @@ public function ticksRandomly() : bool{
 
 	public function onRandomTick() : void{
 		$world = $this->position->getWorld();
+
+		//this property may be updated by canHydrate() - track this so we know if we need to set the block again
+		$oldWaterPositionIndex = $this->waterPositionIndex;
+		$changed = false;
+
 		if(!$this->canHydrate()){
 			if($this->wetness > 0){
 				$event = new FarmlandHydrationChangeEvent($this, $this->wetness, $this->wetness - 1);
@@ -79,9 +125,11 @@ public function onRandomTick() : void{
 				if(!$event->isCancelled()){
 					$this->wetness = $event->getNewHydration();
 					$world->setBlock($this->position, $this, false);
+					$changed = true;
 				}
 			}else{
 				$world->setBlock($this->position, VanillaBlocks::DIRT());
+				$changed = true;
 			}
 		}elseif($this->wetness < self::MAX_WETNESS){
 			$event = new FarmlandHydrationChangeEvent($this, $this->wetness, self::MAX_WETNESS);
@@ -89,8 +137,14 @@ public function onRandomTick() : void{
 			if(!$event->isCancelled()){
 				$this->wetness = $event->getNewHydration();
 				$world->setBlock($this->position, $this, false);
+				$changed = true;
 			}
 		}
+
+		if(!$changed && $oldWaterPositionIndex !== $this->waterPositionIndex){
+			//ensure the water square index is saved regardless of whether anything else happened
+			$world->setBlock($this->position, $this, false);
+		}
 	}
 
 	public function onEntityLand(Entity $entity) : ?float{
@@ -105,19 +159,39 @@ public function onEntityLand(Entity $entity) : ?float{
 	}
 
 	protected function canHydrate() : bool{
-		//TODO: check rain
-		$start = $this->position->add(-4, 0, -4);
-		$end = $this->position->add(4, 1, 4);
-		for($y = $start->y; $y <= $end->y; ++$y){
-			for($z = $start->z; $z <= $end->z; ++$z){
-				for($x = $start->x; $x <= $end->x; ++$x){
-					if($this->position->getWorld()->getBlockAt($x, $y, $z) instanceof Water){
+		$world = $this->position->getWorld();
+
+		$startX = $this->position->getFloorX() - (int) (self::WATER_SEARCH_HORIZONTAL_LENGTH / 2);
+		$startY = $this->position->getFloorY();
+		$startZ = $this->position->getFloorZ() - (int) (self::WATER_SEARCH_HORIZONTAL_LENGTH / 2);
+
+		if($this->waterPositionIndex !== self::WATER_POSITION_INDEX_UNKNOWN){
+			$raw = $this->waterPositionIndex;
+			$x = $raw % self::WATER_SEARCH_HORIZONTAL_LENGTH;
+			$raw = intdiv($raw, self::WATER_SEARCH_HORIZONTAL_LENGTH);
+			$z = $raw % self::WATER_SEARCH_HORIZONTAL_LENGTH;
+			$raw = intdiv($raw, self::WATER_SEARCH_HORIZONTAL_LENGTH);
+			$y = $raw % self::WATER_SEARCH_VERTICAL_LENGTH;
+
+			if($world->getBlockAt($startX + $x, $startY + $y, $startZ + $z) instanceof Water){
+				return true;
+			}
+		}
+
+		//no water found at cached position - search the whole area
+		//y will increment after x/z have been exhausted, as usually water will be at the same Y as the farmland
+		for($y = 0; $y < self::WATER_SEARCH_VERTICAL_LENGTH; $y++){
+			for($x = 0; $x < self::WATER_SEARCH_HORIZONTAL_LENGTH; $x++){
+				for($z = 0; $z < self::WATER_SEARCH_HORIZONTAL_LENGTH; $z++){
+					if($world->getBlockAt($startX + $x, $startY + $y, $startZ + $z) instanceof Water){
+						$this->waterPositionIndex = $x + ($z * self::WATER_SEARCH_HORIZONTAL_LENGTH) + ($y * self::WATER_SEARCH_HORIZONTAL_LENGTH ** 2);
 						return true;
 					}
 				}
 			}
 		}
 
+		$this->waterPositionIndex = self::WATER_POSITION_INDEX_UNKNOWN;
 		return false;
 	}
 
diff --git a/src/block/Fence.php b/src/block/Fence.php
index b57e81ded56..4fa571b675b 100644
--- a/src/block/Fence.php
+++ b/src/block/Fence.php
@@ -42,7 +42,7 @@ public function readStateFromWorld() : Block{
 
 		foreach(Facing::HORIZONTAL as $facing){
 			$block = $this->getSide($facing);
-			if($block instanceof static || $block instanceof FenceGate || $block->getSupportType(Facing::opposite($facing))->equals(SupportType::FULL())){
+			if($block instanceof static || $block instanceof FenceGate || $block->getSupportType(Facing::opposite($facing)) === SupportType::FULL){
 				$this->connections[$facing] = true;
 			}else{
 				unset($this->connections[$facing]);
@@ -98,6 +98,6 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return Facing::axis($facing) === Axis::Y ? SupportType::CENTER() : SupportType::NONE();
+		return Facing::axis($facing) === Axis::Y ? SupportType::CENTER : SupportType::NONE;
 	}
 }
diff --git a/src/block/FenceGate.php b/src/block/FenceGate.php
index c22edabc490..7354564495e 100644
--- a/src/block/FenceGate.php
+++ b/src/block/FenceGate.php
@@ -72,7 +72,7 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	private function checkInWall() : bool{
diff --git a/src/block/FillableCauldron.php b/src/block/FillableCauldron.php
index 84705ea9d56..ceef3529968 100644
--- a/src/block/FillableCauldron.php
+++ b/src/block/FillableCauldron.php
@@ -38,7 +38,7 @@ abstract class FillableCauldron extends Transparent{
 	private int $fillLevel = self::MIN_FILL_LEVEL;
 
 	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
-		$w->boundedInt(3, self::MIN_FILL_LEVEL, self::MAX_FILL_LEVEL, $this->fillLevel);
+		$w->boundedIntAuto(self::MIN_FILL_LEVEL, self::MAX_FILL_LEVEL, $this->fillLevel);
 	}
 
 	public function getFillLevel() : int{ return $this->fillLevel; }
@@ -64,7 +64,7 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return $facing === Facing::UP ? SupportType::EDGE() : SupportType::NONE();
+		return $facing === Facing::UP ? SupportType::EDGE : SupportType::NONE;
 	}
 
 	protected function withFillLevel(int $fillLevel) : Block{
diff --git a/src/block/Fire.php b/src/block/Fire.php
index 11378b82b09..5487c34edeb 100644
--- a/src/block/Fire.php
+++ b/src/block/Fire.php
@@ -23,9 +23,9 @@
 
 namespace pocketmine\block;
 
+use pocketmine\block\utils\AgeableTrait;
 use pocketmine\block\utils\BlockEventHelper;
 use pocketmine\block\utils\SupportType;
-use pocketmine\data\runtime\RuntimeDataDescriber;
 use pocketmine\event\block\BlockBurnEvent;
 use pocketmine\math\Facing;
 use pocketmine\world\format\Chunk;
@@ -36,31 +36,16 @@
 use function mt_rand;
 
 class Fire extends BaseFire{
-	public const MAX_AGE = 15;
-
-	protected int $age = 0;
-
-	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
-		$w->boundedInt(4, 0, self::MAX_AGE, $this->age);
-	}
+	use AgeableTrait;
 
-	public function getAge() : int{ return $this->age; }
-
-	/** @return $this */
-	public function setAge(int $age) : self{
-		if($age < 0 || $age > self::MAX_AGE){
-			throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
-		}
-		$this->age = $age;
-		return $this;
-	}
+	public const MAX_AGE = 15;
 
 	protected function getFireDamage() : int{
 		return 1;
 	}
 
 	private function canBeSupportedBy(Block $block) : bool{
-		return $block->getSupportType(Facing::UP)->equals(SupportType::FULL());
+		return $block->getSupportType(Facing::UP) === SupportType::FULL;
 	}
 
 	public function onNearbyBlockChange() : void{
diff --git a/src/block/FloorCoralFan.php b/src/block/FloorCoralFan.php
index a267a03857d..81ea88186be 100644
--- a/src/block/FloorCoralFan.php
+++ b/src/block/FloorCoralFan.php
@@ -23,6 +23,7 @@
 
 namespace pocketmine\block;
 
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\data\runtime\RuntimeDataDescriber;
 use pocketmine\item\Item;
 use pocketmine\item\VanillaItems;
@@ -35,6 +36,8 @@
 use function rad2deg;
 
 final class FloorCoralFan extends BaseCoral{
+	use StaticSupportTrait;
+
 	private int $axis = Axis::X;
 
 	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
@@ -53,9 +56,6 @@ public function setAxis(int $axis) : self{
 	}
 
 	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		if(!$this->canBeSupportedAt($blockReplace)){
-			return false;
-		}
 		if($player !== null){
 			$playerBlockPos = $player->getPosition()->floor();
 			$directionVector = $blockReplace->getPosition()->subtractVector($playerBlockPos)->normalize();
@@ -73,15 +73,6 @@ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Blo
 		return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
 	}
 
-	public function onNearbyBlockChange() : void{
-		$world = $this->position->getWorld();
-		if(!$this->canBeSupportedAt($this)){
-			$world->useBreakOn($this->position);
-		}else{
-			parent::onNearbyBlockChange();
-		}
-	}
-
 	private function canBeSupportedAt(Block $block) : bool{
 		return $block->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport();
 	}
diff --git a/src/block/Flowable.php b/src/block/Flowable.php
index 2b4e8a02bd2..795fe27561a 100644
--- a/src/block/Flowable.php
+++ b/src/block/Flowable.php
@@ -48,6 +48,6 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 }
diff --git a/src/block/Flower.php b/src/block/Flower.php
index fca5dd98fd5..ef79813fe25 100644
--- a/src/block/Flower.php
+++ b/src/block/Flower.php
@@ -23,28 +23,15 @@
 
 namespace pocketmine\block;
 
-use pocketmine\item\Item;
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\math\Facing;
-use pocketmine\math\Vector3;
-use pocketmine\player\Player;
-use pocketmine\world\BlockTransaction;
 
 class Flower extends Flowable{
+	use StaticSupportTrait;
 
-	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		$down = $this->getSide(Facing::DOWN);
-		if($down->hasTypeTag(BlockTypeTags::DIRT) || $down->hasTypeTag(BlockTypeTags::MUD)){
-			return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
-		}
-
-		return false;
-	}
-
-	public function onNearbyBlockChange() : void{
-		$down = $this->getSide(Facing::DOWN);
-		if(!$down->hasTypeTag(BlockTypeTags::DIRT) && !$down->hasTypeTag(BlockTypeTags::MUD)){
-			$this->position->getWorld()->useBreakOn($this->position);
-		}
+	private function canBeSupportedAt(Block $block) : bool{
+		$supportBlock = $block->getSide(Facing::DOWN);
+		return $supportBlock->hasTypeTag(BlockTypeTags::DIRT) || $supportBlock->hasTypeTag(BlockTypeTags::MUD);
 	}
 
 	public function getFlameEncouragement() : int{
diff --git a/src/block/FlowerPot.php b/src/block/FlowerPot.php
index 4e4dbfa6e15..fb3e78d82ad 100644
--- a/src/block/FlowerPot.php
+++ b/src/block/FlowerPot.php
@@ -24,15 +24,16 @@
 namespace pocketmine\block;
 
 use pocketmine\block\tile\FlowerPot as TileFlowerPot;
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\item\Item;
 use pocketmine\math\AxisAlignedBB;
 use pocketmine\math\Facing;
 use pocketmine\math\Vector3;
 use pocketmine\player\Player;
-use pocketmine\world\BlockTransaction;
 use function assert;
 
 class FlowerPot extends Flowable{
+	use StaticSupportTrait;
 
 	protected ?Block $plant = null;
 
@@ -89,20 +90,6 @@ protected function recalculateCollisionBoxes() : array{
 		return [AxisAlignedBB::one()->contract(3 / 16, 0, 3 / 16)->trim(Facing::UP, 5 / 8)];
 	}
 
-	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		if(!$this->canBeSupportedAt($blockReplace)){
-			return false;
-		}
-
-		return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
-	}
-
-	public function onNearbyBlockChange() : void{
-		if(!$this->canBeSupportedAt($this)){
-			$this->position->getWorld()->useBreakOn($this->position);
-		}
-	}
-
 	private function canBeSupportedAt(Block $block) : bool{
 		return $block->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport();
 	}
diff --git a/src/block/Froglight.php b/src/block/Froglight.php
index f33fc82613f..562b33f174e 100644
--- a/src/block/Froglight.php
+++ b/src/block/Froglight.php
@@ -28,15 +28,10 @@
 
 final class Froglight extends SimplePillar{
 
-	private FroglightType $froglightType;
-
-	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->froglightType = FroglightType::OCHRE();
-		parent::__construct($idInfo, $name, $typeInfo);
-	}
+	private FroglightType $froglightType = FroglightType::OCHRE;
 
 	public function describeBlockItemState(RuntimeDataDescriber $w) : void{
-		$w->froglightType($this->froglightType);
+		$w->enum($this->froglightType);
 	}
 
 	public function getFroglightType() : FroglightType{ return $this->froglightType; }
diff --git a/src/block/FrostedIce.php b/src/block/FrostedIce.php
index 039fe49f36a..3e859230678 100644
--- a/src/block/FrostedIce.php
+++ b/src/block/FrostedIce.php
@@ -23,29 +23,14 @@
 
 namespace pocketmine\block;
 
+use pocketmine\block\utils\AgeableTrait;
 use pocketmine\block\utils\BlockEventHelper;
-use pocketmine\data\runtime\RuntimeDataDescriber;
 use function mt_rand;
 
 class FrostedIce extends Ice{
-	public const MAX_AGE = 3;
-
-	protected int $age = 0;
-
-	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
-		$w->boundedInt(2, 0, self::MAX_AGE, $this->age);
-	}
+	use AgeableTrait;
 
-	public function getAge() : int{ return $this->age; }
-
-	/** @return $this */
-	public function setAge(int $age) : self{
-		if($age < 0 || $age > self::MAX_AGE){
-			throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
-		}
-		$this->age = $age;
-		return $this;
-	}
+	public const MAX_AGE = 3;
 
 	public function onNearbyBlockChange() : void{
 		$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(20, 40));
diff --git a/src/block/GlazedTerracotta.php b/src/block/GlazedTerracotta.php
index b49347aef0b..15b3254e5d9 100644
--- a/src/block/GlazedTerracotta.php
+++ b/src/block/GlazedTerracotta.php
@@ -24,15 +24,9 @@
 namespace pocketmine\block;
 
 use pocketmine\block\utils\ColoredTrait;
-use pocketmine\block\utils\DyeColor;
 use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
 
 class GlazedTerracotta extends Opaque{
 	use ColoredTrait;
 	use FacesOppositePlacingPlayerTrait;
-
-	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->color = DyeColor::BLACK();
-		parent::__construct($idInfo, $name, $typeInfo);
-	}
 }
diff --git a/src/block/GlowLichen.php b/src/block/GlowLichen.php
index 84dec29ce97..de66ccad769 100644
--- a/src/block/GlowLichen.php
+++ b/src/block/GlowLichen.php
@@ -95,7 +95,7 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	public function canBeReplaced() : bool{
@@ -121,7 +121,7 @@ public function onNearbyBlockChange() : void{
 		$changed = false;
 
 		foreach($this->faces as $face){
-			if(!$this->getAdjacentSupportType($face)->equals(SupportType::FULL())){
+			if($this->getAdjacentSupportType($face) !== SupportType::FULL){
 				unset($this->faces[$face]);
 				$changed = true;
 			}
@@ -156,7 +156,7 @@ private function spread(World $world, Vector3 $replacePos, int $spreadFace) : bo
 		$supportBlock = $world->getBlock($replacePos->getSide($spreadFace));
 		$supportFace = Facing::opposite($spreadFace);
 
-		if(!$supportBlock->getSupportType($supportFace)->equals(SupportType::FULL())){
+		if($supportBlock->getSupportType($supportFace) !== SupportType::FULL){
 			return false;
 		}
 
@@ -268,7 +268,7 @@ public function getFlammability() : int{
 	private function getAvailableFaces() : array{
 		$faces = [];
 		foreach(Facing::ALL as $face){
-			if(!$this->hasFace($face) && $this->getAdjacentSupportType($face)->equals(SupportType::FULL())){
+			if(!$this->hasFace($face) && $this->getAdjacentSupportType($face) === SupportType::FULL){
 				$faces[$face] = $face;
 			}
 		}
diff --git a/src/block/Grass.php b/src/block/Grass.php
index 709dc6a9de2..db2f8b922b8 100644
--- a/src/block/Grass.php
+++ b/src/block/Grass.php
@@ -69,7 +69,7 @@ public function onRandomTick() : void{
 				$b = $world->getBlockAt($x, $y, $z);
 				if(
 					!($b instanceof Dirt) ||
-					!$b->getDirtType()->equals(DirtType::NORMAL()) ||
+					$b->getDirtType() !== DirtType::NORMAL ||
 					$world->getFullLightAt($x, $y + 1, $z) < 4 ||
 					$world->getBlockAt($x, $y + 1, $z)->getLightFilter() >= 2
 				){
diff --git a/src/block/HangingRoots.php b/src/block/HangingRoots.php
index 0e03f4468f1..9c7bfd09601 100644
--- a/src/block/HangingRoots.php
+++ b/src/block/HangingRoots.php
@@ -23,32 +23,18 @@
 
 namespace pocketmine\block;
 
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\item\enchantment\VanillaEnchantments;
 use pocketmine\item\Item;
 use pocketmine\math\Facing;
-use pocketmine\math\Vector3;
-use pocketmine\player\Player;
-use pocketmine\world\BlockTransaction;
 
 final class HangingRoots extends Flowable{
+	use StaticSupportTrait;
 
 	private function canBeSupportedAt(Block $block) : bool{
 		return $block->getAdjacentSupportType(Facing::UP)->hasCenterSupport(); //weird I know, but they can be placed on the bottom of fences
 	}
 
-	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		if(!$this->canBeSupportedAt($blockReplace)){
-			return false;
-		}
-		return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
-	}
-
-	public function onNearbyBlockChange() : void{
-		if(!$this->canBeSupportedAt($this)){
-			$this->position->getWorld()->useBreakOn($this->position);
-		}
-	}
-
 	public function getDropsForIncompatibleTool(Item $item) : array{
 		if($item->hasEnchantment(VanillaEnchantments::SILK_TOUCH())){
 			return $this->getDropsForCompatibleTool($item);
diff --git a/src/block/Hopper.php b/src/block/Hopper.php
index ea000503cb0..0d823674b23 100644
--- a/src/block/Hopper.php
+++ b/src/block/Hopper.php
@@ -68,9 +68,9 @@ protected function recalculateCollisionBoxes() : array{
 
 	public function getSupportType(int $facing) : SupportType{
 		return match($facing){
-			Facing::UP => SupportType::FULL(),
-			Facing::DOWN => $this->facing === Facing::DOWN ? SupportType::CENTER() : SupportType::NONE(),
-			default => SupportType::NONE()
+			Facing::UP => SupportType::FULL,
+			Facing::DOWN => $this->facing === Facing::DOWN ? SupportType::CENTER : SupportType::NONE,
+			default => SupportType::NONE
 		};
 	}
 
diff --git a/src/block/ItemFrame.php b/src/block/ItemFrame.php
index 21bc5c20ad0..b5b6093c497 100644
--- a/src/block/ItemFrame.php
+++ b/src/block/ItemFrame.php
@@ -164,7 +164,7 @@ public function onAttack(Item $item, int $face, ?Player $player = null) : bool{
 	}
 
 	private function canBeSupportedAt(Block $block, int $face) : bool{
-		return !$block->getAdjacentSupportType($face)->equals(SupportType::NONE());
+		return $block->getAdjacentSupportType($face) !== SupportType::NONE;
 	}
 
 	public function onNearbyBlockChange() : void{
diff --git a/src/block/Ladder.php b/src/block/Ladder.php
index 83adada8283..58f133f6ecf 100644
--- a/src/block/Ladder.php
+++ b/src/block/Ladder.php
@@ -66,7 +66,7 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
@@ -85,6 +85,6 @@ public function onNearbyBlockChange() : void{
 	}
 
 	private function canBeSupportedAt(Block $block, int $face) : bool{
-		return $block->getAdjacentSupportType($face)->equals(SupportType::FULL());
+		return $block->getAdjacentSupportType($face) === SupportType::FULL;
 	}
 }
diff --git a/src/block/Lantern.php b/src/block/Lantern.php
index 8ebc8ba2ca4..e9cbcc3fe97 100644
--- a/src/block/Lantern.php
+++ b/src/block/Lantern.php
@@ -73,7 +73,7 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
diff --git a/src/block/Leaves.php b/src/block/Leaves.php
index b1839dca6bf..7fe9eae74f2 100644
--- a/src/block/Leaves.php
+++ b/src/block/Leaves.php
@@ -33,12 +33,13 @@
 use pocketmine\math\Facing;
 use pocketmine\math\Vector3;
 use pocketmine\player\Player;
-use pocketmine\utils\AssumptionFailedError;
 use pocketmine\world\BlockTransaction;
 use pocketmine\world\World;
 use function mt_rand;
 
 class Leaves extends Transparent{
+	private const MAX_LOG_DISTANCE = 4;
+
 	protected LeavesType $leavesType; //immutable for now
 	protected bool $noDecay = false;
 	protected bool $checkDecay = false;
@@ -92,7 +93,7 @@ protected function findLog(Vector3 $pos, array &$visited = [], int $distance = 0
 			return true;
 		}
 
-		if($block instanceof Leaves && $distance <= 4){
+		if($block instanceof Leaves && $distance <= self::MAX_LOG_DISTANCE){
 			foreach(Facing::ALL as $side){
 				if($this->findLog($pos->getSide($side), $visited, $distance + 1)){
 					return true;
@@ -111,7 +112,7 @@ public function onNearbyBlockChange() : void{
 	}
 
 	public function ticksRandomly() : bool{
-		return true;
+		return !$this->noDecay && $this->checkDecay;
 	}
 
 	public function onRandomTick() : void{
@@ -147,23 +148,22 @@ public function getDropsForCompatibleTool(Item $item) : array{
 		if(FortuneDropHelper::bonusChanceDivisor($item, 20, 4)){ //Saplings
 			// TODO: according to the wiki, the jungle saplings have a different drop rate
 			$sapling = (match($this->leavesType){
-				LeavesType::ACACIA() => VanillaBlocks::ACACIA_SAPLING(),
-				LeavesType::BIRCH() => VanillaBlocks::BIRCH_SAPLING(),
-				LeavesType::DARK_OAK() => VanillaBlocks::DARK_OAK_SAPLING(),
-				LeavesType::JUNGLE() => VanillaBlocks::JUNGLE_SAPLING(),
-				LeavesType::OAK() => VanillaBlocks::OAK_SAPLING(),
-				LeavesType::SPRUCE() => VanillaBlocks::SPRUCE_SAPLING(),
-				LeavesType::MANGROVE(), //TODO: mangrove propagule
-				LeavesType::AZALEA(), LeavesType::FLOWERING_AZALEA() => null, //TODO: azalea
-				LeavesType::CHERRY() => null, //TODO: cherry
-				default => throw new AssumptionFailedError("Unreachable")
+				LeavesType::ACACIA => VanillaBlocks::ACACIA_SAPLING(),
+				LeavesType::BIRCH => VanillaBlocks::BIRCH_SAPLING(),
+				LeavesType::DARK_OAK => VanillaBlocks::DARK_OAK_SAPLING(),
+				LeavesType::JUNGLE => VanillaBlocks::JUNGLE_SAPLING(),
+				LeavesType::OAK => VanillaBlocks::OAK_SAPLING(),
+				LeavesType::SPRUCE => VanillaBlocks::SPRUCE_SAPLING(),
+				LeavesType::MANGROVE, //TODO: mangrove propagule
+				LeavesType::AZALEA, LeavesType::FLOWERING_AZALEA => null, //TODO: azalea
+				LeavesType::CHERRY => null, //TODO: cherry
 			})?->asItem();
 			if($sapling !== null){
 				$drops[] = $sapling;
 			}
 		}
 		if(
-			($this->leavesType->equals(LeavesType::OAK()) || $this->leavesType->equals(LeavesType::DARK_OAK())) &&
+			($this->leavesType === LeavesType::OAK || $this->leavesType === LeavesType::DARK_OAK) &&
 			FortuneDropHelper::bonusChanceDivisor($item, 200, 20)
 		){ //Apples
 			$drops[] = VanillaItems::APPLE();
@@ -188,6 +188,6 @@ public function getFlammability() : int{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 }
diff --git a/src/block/Lectern.php b/src/block/Lectern.php
index d9f07d22b96..03880b3c5f1 100644
--- a/src/block/Lectern.php
+++ b/src/block/Lectern.php
@@ -87,7 +87,7 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	public function isProducingSignal() : bool{ return $this->producingSignal; }
diff --git a/src/block/Lever.php b/src/block/Lever.php
index e4b8c0811dd..d2b98efc34f 100644
--- a/src/block/Lever.php
+++ b/src/block/Lever.php
@@ -36,16 +36,11 @@
 use pocketmine\world\sound\RedstonePowerOnSound;
 
 class Lever extends Flowable{
-	protected LeverFacing $facing;
+	protected LeverFacing $facing = LeverFacing::UP_AXIS_X;
 	protected bool $activated = false;
 
-	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->facing = LeverFacing::UP_AXIS_X();
-		parent::__construct($idInfo, $name, $typeInfo);
-	}
-
 	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
-		$w->leverFacing($this->facing);
+		$w->enum($this->facing);
 		$w->bool($this->activated);
 	}
 
@@ -77,12 +72,12 @@ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Blo
 			return $x;
 		};
 		$this->facing = match($face){
-			Facing::DOWN => $selectUpDownPos(LeverFacing::DOWN_AXIS_X(), LeverFacing::DOWN_AXIS_Z()),
-			Facing::UP => $selectUpDownPos(LeverFacing::UP_AXIS_X(), LeverFacing::UP_AXIS_Z()),
-			Facing::NORTH => LeverFacing::NORTH(),
-			Facing::SOUTH => LeverFacing::SOUTH(),
-			Facing::WEST => LeverFacing::WEST(),
-			Facing::EAST => LeverFacing::EAST(),
+			Facing::DOWN => $selectUpDownPos(LeverFacing::DOWN_AXIS_X, LeverFacing::DOWN_AXIS_Z),
+			Facing::UP => $selectUpDownPos(LeverFacing::UP_AXIS_X, LeverFacing::UP_AXIS_Z),
+			Facing::NORTH => LeverFacing::NORTH,
+			Facing::SOUTH => LeverFacing::SOUTH,
+			Facing::WEST => LeverFacing::WEST,
+			Facing::EAST => LeverFacing::EAST,
 			default => throw new AssumptionFailedError("Bad facing value"),
 		};
 
diff --git a/src/block/Light.php b/src/block/Light.php
index 239e1c04855..29a3a8dfca7 100644
--- a/src/block/Light.php
+++ b/src/block/Light.php
@@ -35,7 +35,7 @@ final class Light extends Flowable{
 	private int $level = self::MAX_LIGHT_LEVEL;
 
 	public function describeBlockItemState(RuntimeDataDescriber $w) : void{
-		$w->boundedInt(4, self::MIN_LIGHT_LEVEL, self::MAX_LIGHT_LEVEL, $this->level);
+		$w->boundedIntAuto(self::MIN_LIGHT_LEVEL, self::MAX_LIGHT_LEVEL, $this->level);
 	}
 
 	public function getLightLevel() : int{ return $this->level; }
diff --git a/src/block/Liquid.php b/src/block/Liquid.php
index e102540ecfb..6404cf9081f 100644
--- a/src/block/Liquid.php
+++ b/src/block/Liquid.php
@@ -49,7 +49,7 @@ abstract class Liquid extends Transparent{
 	protected bool $still = false;
 
 	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
-		$w->boundedInt(3, 0, self::MAX_DECAY, $this->decay);
+		$w->boundedIntAuto(0, self::MAX_DECAY, $this->decay);
 		$w->bool($this->falling);
 		$w->bool($this->still);
 	}
@@ -97,7 +97,7 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	public function getDropsForCompatibleTool(Item $item) : array{
@@ -165,23 +165,22 @@ public function getFlowVector() : Vector3{
 
 		$vX = $vY = $vZ = 0;
 
+		$x = $this->position->getFloorX();
+		$y = $this->position->getFloorY();
+		$z = $this->position->getFloorZ();
+
 		$decay = $this->getEffectiveFlowDecay($this);
 
 		$world = $this->position->getWorld();
 
 		foreach(Facing::HORIZONTAL as $j){
-			$x = $this->position->x;
-			$y = $this->position->y;
-			$z = $this->position->z;
-
-			match($j){
-				Facing::WEST => --$x,
-				Facing::EAST => ++$x,
-				Facing::NORTH => --$z,
-				Facing::SOUTH => ++$z
-			};
-
-			$sideBlock = $world->getBlockAt($x, $y, $z);
+			[$dx, $dy, $dz] = Facing::OFFSET[$j];
+
+			$sideX = $x + $dx;
+			$sideY = $y + $dy;
+			$sideZ = $z + $dz;
+
+			$sideBlock = $world->getBlockAt($sideX, $sideY, $sideZ);
 			$blockDecay = $this->getEffectiveFlowDecay($sideBlock);
 
 			if($blockDecay < 0){
@@ -189,21 +188,21 @@ public function getFlowVector() : Vector3{
 					continue;
 				}
 
-				$blockDecay = $this->getEffectiveFlowDecay($world->getBlockAt($x, $y - 1, $z));
+				$blockDecay = $this->getEffectiveFlowDecay($world->getBlockAt($sideX, $sideY - 1, $sideZ));
 
 				if($blockDecay >= 0){
 					$realDecay = $blockDecay - ($decay - 8);
-					$vX += ($x - $this->position->x) * $realDecay;
-					$vY += ($y - $this->position->y) * $realDecay;
-					$vZ += ($z - $this->position->z) * $realDecay;
+					$vX += $dx * $realDecay;
+					$vY += $dy * $realDecay;
+					$vZ += $dz * $realDecay;
 				}
 
 				continue;
 			}else{
 				$realDecay = $blockDecay - $decay;
-				$vX += ($x - $this->position->x) * $realDecay;
-				$vY += ($y - $this->position->y) * $realDecay;
-				$vZ += ($z - $this->position->z) * $realDecay;
+				$vX += $dx * $realDecay;
+				$vY += $dy * $realDecay;
+				$vZ += $dz * $realDecay;
 			}
 		}
 
@@ -211,10 +210,10 @@ public function getFlowVector() : Vector3{
 
 		if($this->falling){
 			foreach(Facing::HORIZONTAL as $facing){
-				$pos = $this->position->getSide($facing);
+				[$dx, $dy, $dz] = Facing::OFFSET[$facing];
 				if(
-					!$this->canFlowInto($world->getBlockAt($pos->x, $pos->y, $pos->z)) ||
-					!$this->canFlowInto($world->getBlockAt($pos->x, $pos->y + 1, $pos->z))
+					!$this->canFlowInto($world->getBlockAt($x + $dx, $y + $dy, $z + $dz)) ||
+					!$this->canFlowInto($world->getBlockAt($x + $dx, $y + $dy + 1, $z + $dz))
 				){
 					$vector = $vector->normalize()->add(0, -6, 0);
 					break;
@@ -260,13 +259,17 @@ public function onScheduledUpdate() : void{
 
 		$world = $this->position->getWorld();
 
+		$x = $this->position->getFloorX();
+		$y = $this->position->getFloorY();
+		$z = $this->position->getFloorZ();
+
 		if(!$this->isSource()){
 			$smallestFlowDecay = -100;
 			$this->adjacentSources = 0;
-			$smallestFlowDecay = $this->getSmallestFlowDecay($world->getBlockAt($this->position->x, $this->position->y, $this->position->z - 1), $smallestFlowDecay);
-			$smallestFlowDecay = $this->getSmallestFlowDecay($world->getBlockAt($this->position->x, $this->position->y, $this->position->z + 1), $smallestFlowDecay);
-			$smallestFlowDecay = $this->getSmallestFlowDecay($world->getBlockAt($this->position->x - 1, $this->position->y, $this->position->z), $smallestFlowDecay);
-			$smallestFlowDecay = $this->getSmallestFlowDecay($world->getBlockAt($this->position->x + 1, $this->position->y, $this->position->z), $smallestFlowDecay);
+			$smallestFlowDecay = $this->getSmallestFlowDecay($world->getBlockAt($x, $y, $z - 1), $smallestFlowDecay);
+			$smallestFlowDecay = $this->getSmallestFlowDecay($world->getBlockAt($x, $y, $z + 1), $smallestFlowDecay);
+			$smallestFlowDecay = $this->getSmallestFlowDecay($world->getBlockAt($x - 1, $y, $z), $smallestFlowDecay);
+			$smallestFlowDecay = $this->getSmallestFlowDecay($world->getBlockAt($x + 1, $y, $z), $smallestFlowDecay);
 
 			$newDecay = $smallestFlowDecay + $multiplier;
 			$falling = false;
@@ -275,13 +278,13 @@ public function onScheduledUpdate() : void{
 				$newDecay = -1;
 			}
 
-			if($this->getEffectiveFlowDecay($world->getBlockAt($this->position->x, $this->position->y + 1, $this->position->z)) >= 0){
+			if($this->getEffectiveFlowDecay($world->getBlockAt($x, $y + 1, $z)) >= 0){
 				$falling = true;
 			}
 
 			$minAdjacentSources = $this->getMinAdjacentSourcesToFormSource();
 			if($minAdjacentSources !== null && $this->adjacentSources >= $minAdjacentSources){
-				$bottomBlock = $world->getBlockAt($this->position->x, $this->position->y - 1, $this->position->z);
+				$bottomBlock = $world->getBlockAt($x, $y - 1, $z);
 				if($bottomBlock->isSolid() || ($bottomBlock instanceof Liquid && $bottomBlock->hasSameTypeId($this) && $bottomBlock->isSource())){
 					$newDecay = 0;
 					$falling = false;
@@ -290,17 +293,17 @@ public function onScheduledUpdate() : void{
 
 			if($falling !== $this->falling || (!$falling && $newDecay !== $this->decay)){
 				if(!$falling && $newDecay < 0){
-					$world->setBlock($this->position, VanillaBlocks::AIR());
+					$world->setBlockAt($x, $y, $z, VanillaBlocks::AIR());
 					return;
 				}
 
 				$this->falling = $falling;
 				$this->decay = $falling ? 0 : $newDecay;
-				$world->setBlock($this->position, $this); //local block update will cause an update to be scheduled
+				$world->setBlockAt($x, $y, $z, $this); //local block update will cause an update to be scheduled
 			}
 		}
 
-		$bottomBlock = $world->getBlockAt($this->position->x, $this->position->y - 1, $this->position->z);
+		$bottomBlock = $world->getBlockAt($x, $y - 1, $z);
 
 		$this->flowIntoBlock($bottomBlock, 0, true);
 
@@ -313,8 +316,9 @@ public function onScheduledUpdate() : void{
 
 			if($adjacentDecay <= self::MAX_DECAY){
 				$calculator = new MinimumCostFlowCalculator($world, $this->getFlowDecayPerBlock(), $this->canFlowInto(...));
-				foreach($calculator->getOptimalFlowDirections($this->position->getFloorX(), $this->position->getFloorY(), $this->position->getFloorZ()) as $facing){
-					$this->flowIntoBlock($world->getBlock($this->position->getSide($facing)), $adjacentDecay, false);
+				foreach($calculator->getOptimalFlowDirections($x, $y, $z) as $facing){
+					[$dx, $dy, $dz] = Facing::OFFSET[$facing];
+					$this->flowIntoBlock($world->getBlockAt($x + $dx, $y + $dy, $z + $dz), $adjacentDecay, false);
 				}
 			}
 		}
diff --git a/src/block/MobHead.php b/src/block/MobHead.php
index 96cd1cf344d..f4e945841d2 100644
--- a/src/block/MobHead.php
+++ b/src/block/MobHead.php
@@ -39,18 +39,13 @@ class MobHead extends Flowable{
 	public const MIN_ROTATION = 0;
 	public const MAX_ROTATION = 15;
 
-	protected MobHeadType $mobHeadType;
+	protected MobHeadType $mobHeadType = MobHeadType::SKELETON;
 
 	protected int $facing = Facing::NORTH;
 	protected int $rotation = self::MIN_ROTATION; //TODO: split this into floor skull and wall skull handling
 
-	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->mobHeadType = MobHeadType::SKELETON(); //TODO: this should be a parameter
-		parent::__construct($idInfo, $name, $typeInfo);
-	}
-
 	public function describeBlockItemState(RuntimeDataDescriber $w) : void{
-		$w->mobHeadType($this->mobHeadType);
+		$w->enum($this->mobHeadType);
 	}
 
 	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
diff --git a/src/block/MonsterSpawner.php b/src/block/MonsterSpawner.php
index 129da33946a..5cbe80e0a81 100644
--- a/src/block/MonsterSpawner.php
+++ b/src/block/MonsterSpawner.php
@@ -42,6 +42,6 @@ public function onScheduledUpdate() : void{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 }
diff --git a/src/block/Mycelium.php b/src/block/Mycelium.php
index 08b0b3e750e..05b63d9bff0 100644
--- a/src/block/Mycelium.php
+++ b/src/block/Mycelium.php
@@ -52,7 +52,7 @@ public function onRandomTick() : void{
 		$z = mt_rand($this->position->z - 1, $this->position->z + 1);
 		$world = $this->position->getWorld();
 		$block = $world->getBlockAt($x, $y, $z);
-		if($block instanceof Dirt && $block->getDirtType()->equals(DirtType::NORMAL())){
+		if($block instanceof Dirt && $block->getDirtType() === DirtType::NORMAL){
 			if($block->getSide(Facing::UP) instanceof Transparent){
 				BlockEventHelper::spread($block, VanillaBlocks::MYCELIUM(), $this);
 			}
diff --git a/src/block/NetherPortal.php b/src/block/NetherPortal.php
index a2524914abd..6a45fb7a0ac 100644
--- a/src/block/NetherPortal.php
+++ b/src/block/NetherPortal.php
@@ -70,7 +70,7 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	public function getDrops(Item $item) : array{
diff --git a/src/block/NetherRoots.php b/src/block/NetherRoots.php
new file mode 100644
index 00000000000..5c39b8bec3e
--- /dev/null
+++ b/src/block/NetherRoots.php
@@ -0,0 +1,39 @@
+<?php
+
+/*
+ *
+ *  ____            _        _   __  __ _                  __  __ ____
+ * |  _ \ ___   ___| | _____| |_|  \/  (_)_ __   ___      |  \/  |  _ \
+ * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
+ * |  __/ (_) | (__|   <  __/ |_| |  | | | | | |  __/_____| |  | |  __/
+ * |_|   \___/ \___|_|\_\___|\__|_|  |_|_|_| |_|\___|     |_|  |_|_|
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * @author PocketMine Team
+ * @link http://www.pocketmine.net/
+ *
+ *
+ */
+
+declare(strict_types=1);
+
+namespace pocketmine\block;
+
+use pocketmine\block\utils\StaticSupportTrait;
+use pocketmine\math\Facing;
+
+final class NetherRoots extends Flowable{
+	use StaticSupportTrait;
+
+	private function canBeSupportedAt(Block $block) : bool{
+		//TODO: nylium, moss
+		$supportBlock = $block->getSide(Facing::DOWN);
+		return
+			$supportBlock->hasTypeTag(BlockTypeTags::DIRT) ||
+			$supportBlock->hasTypeTag(BlockTypeTags::MUD);
+	}
+}
diff --git a/src/block/NetherVines.php b/src/block/NetherVines.php
index c78000fa1ef..eb459a0f1e3 100644
--- a/src/block/NetherVines.php
+++ b/src/block/NetherVines.php
@@ -23,9 +23,10 @@
 
 namespace pocketmine\block;
 
+use pocketmine\block\utils\AgeableTrait;
 use pocketmine\block\utils\FortuneDropHelper;
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\block\utils\SupportType;
-use pocketmine\data\runtime\RuntimeDataDescriber;
 use pocketmine\entity\Entity;
 use pocketmine\event\block\StructureGrowEvent;
 use pocketmine\item\Fertilizer;
@@ -41,44 +42,23 @@
  * This class is used for Weeping & Twisting vines, because they have same behaviour
  */
 class NetherVines extends Flowable{
+	use AgeableTrait;
+	use StaticSupportTrait;
+
 	public const MAX_AGE = 25;
 
 	/** Direction the vine grows towards. */
 	private int $growthFace;
 
-	protected int $age = 0;
-
 	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo, int $growthFace){
 		$this->growthFace = $growthFace;
 		parent::__construct($idInfo, $name, $typeInfo);
 	}
 
-	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
-		$w->boundedInt(5, 0, self::MAX_AGE, $this->age);
-	}
-
-	public function getAge() : int{
-		return $this->age;
-	}
-
-	/** @return $this */
-	public function setAge(int $age) : self{
-		if($age < 0 || $age > self::MAX_AGE){
-			throw new \InvalidArgumentException("Age must be in range 0-" . self::MAX_AGE);
-		}
-
-		$this->age = $age;
-		return $this;
-	}
-
 	public function isAffectedBySilkTouch() : bool{
 		return true;
 	}
 
-	public function ticksRandomly() : bool{
-		return true;
-	}
-
 	public function canClimb() : bool{
 		return true;
 	}
@@ -88,12 +68,6 @@ private function canBeSupportedAt(Block $block) : bool{
 		return $supportBlock->getSupportType($this->growthFace)->hasCenterSupport() || $supportBlock->hasSameTypeId($this);
 	}
 
-	public function onNearbyBlockChange() : void{
-		if(!$this->canBeSupportedAt($this)){
-			$this->position->getWorld()->useBreakOn($this->position);
-		}
-	}
-
 	/**
 	 * Returns the block at the end of the vine structure furthest from the supporting block.
 	 */
@@ -106,9 +80,6 @@ private function seekToTip() : NetherVines{
 	}
 
 	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		if(!$this->canBeSupportedAt($blockReplace)){
-			return false;
-		}
 		$this->age = mt_rand(0, self::MAX_AGE - 1);
 		return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
 	}
@@ -123,8 +94,12 @@ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player
 		return false;
 	}
 
+	public function ticksRandomly() : bool{
+		return $this->age < self::MAX_AGE;
+	}
+
 	public function onRandomTick() : void{
-		if(mt_rand(1, 10) === 1 && $this->age < self::MAX_AGE){
+		if($this->age < self::MAX_AGE && mt_rand(1, 10) === 1){
 			if($this->getSide($this->growthFace)->canBeReplaced()){
 				$this->grow(null);
 			}
@@ -184,6 +159,6 @@ public function getDropsForCompatibleTool(Item $item) : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 }
diff --git a/src/block/NetherWartPlant.php b/src/block/NetherWartPlant.php
index d7e58744172..34e6fd57ece 100644
--- a/src/block/NetherWartPlant.php
+++ b/src/block/NetherWartPlant.php
@@ -23,53 +23,26 @@
 
 namespace pocketmine\block;
 
+use pocketmine\block\utils\AgeableTrait;
 use pocketmine\block\utils\BlockEventHelper;
 use pocketmine\block\utils\FortuneDropHelper;
-use pocketmine\data\runtime\RuntimeDataDescriber;
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\item\Item;
 use pocketmine\math\Facing;
-use pocketmine\math\Vector3;
-use pocketmine\player\Player;
-use pocketmine\world\BlockTransaction;
 use function mt_rand;
 
 class NetherWartPlant extends Flowable{
-	public const MAX_AGE = 3;
-
-	protected int $age = 0;
-
-	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
-		$w->boundedInt(2, 0, self::MAX_AGE, $this->age);
-	}
+	use AgeableTrait;
+	use StaticSupportTrait;
 
-	public function getAge() : int{ return $this->age; }
-
-	/** @return $this */
-	public function setAge(int $age) : self{
-		if($age < 0 || $age > self::MAX_AGE){
-			throw new \InvalidArgumentException("Age must be in range 0 ..." . self::MAX_AGE);
-		}
-		$this->age = $age;
-		return $this;
-	}
-
-	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		$down = $this->getSide(Facing::DOWN);
-		if($down->getTypeId() === BlockTypeIds::SOUL_SAND){
-			return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
-		}
-
-		return false;
-	}
+	public const MAX_AGE = 3;
 
-	public function onNearbyBlockChange() : void{
-		if($this->getSide(Facing::DOWN)->getTypeId() !== BlockTypeIds::SOUL_SAND){
-			$this->position->getWorld()->useBreakOn($this->position);
-		}
+	private function canBeSupportedAt(Block $block) : bool{
+		return $block->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::SOUL_SAND;
 	}
 
 	public function ticksRandomly() : bool{
-		return true;
+		return $this->age < self::MAX_AGE;
 	}
 
 	public function onRandomTick() : void{
diff --git a/src/block/PinkPetals.php b/src/block/PinkPetals.php
index 9271e7ddc61..872798df051 100644
--- a/src/block/PinkPetals.php
+++ b/src/block/PinkPetals.php
@@ -25,6 +25,7 @@
 
 use pocketmine\block\utils\BlockEventHelper;
 use pocketmine\block\utils\HorizontalFacingTrait;
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\data\runtime\RuntimeDataDescriber;
 use pocketmine\item\Fertilizer;
 use pocketmine\item\Item;
@@ -35,6 +36,9 @@
 
 class PinkPetals extends Flowable{
 	use HorizontalFacingTrait;
+	use StaticSupportTrait {
+		canBePlacedAt as supportedWhenPlacedAt;
+	}
 
 	public const MIN_COUNT = 1;
 	public const MAX_COUNT = 4;
@@ -43,7 +47,7 @@ class PinkPetals extends Flowable{
 
 	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
 		$w->horizontalFacing($this->facing);
-		$w->boundedInt(2, self::MIN_COUNT, self::MAX_COUNT, $this->count);
+		$w->boundedIntAuto(self::MIN_COUNT, self::MAX_COUNT, $this->count);
 	}
 
 	public function getCount() : int{
@@ -65,20 +69,11 @@ private function canBeSupportedAt(Block $block) : bool{
 		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);
+		return ($blockReplace instanceof PinkPetals && $blockReplace->getCount() < self::MAX_COUNT) || $this->supportedWhenPlacedAt($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();
diff --git a/src/block/PitcherCrop.php b/src/block/PitcherCrop.php
new file mode 100644
index 00000000000..2d9a021620b
--- /dev/null
+++ b/src/block/PitcherCrop.php
@@ -0,0 +1,105 @@
+<?php
+
+/*
+ *
+ *  ____            _        _   __  __ _                  __  __ ____
+ * |  _ \ ___   ___| | _____| |_|  \/  (_)_ __   ___      |  \/  |  _ \
+ * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
+ * |  __/ (_) | (__|   <  __/ |_| |  | | | | | |  __/_____| |  | |  __/
+ * |_|   \___/ \___|_|\_\___|\__|_|  |_|_|_| |_|\___|     |_|  |_|_|
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * @author PocketMine Team
+ * @link http://www.pocketmine.net/
+ *
+ *
+ */
+
+declare(strict_types=1);
+
+namespace pocketmine\block;
+
+use pocketmine\block\utils\AgeableTrait;
+use pocketmine\block\utils\BlockEventHelper;
+use pocketmine\block\utils\StaticSupportTrait;
+use pocketmine\event\block\StructureGrowEvent;
+use pocketmine\item\Fertilizer;
+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 function mt_rand;
+
+final class PitcherCrop extends Flowable{
+	use AgeableTrait;
+	use StaticSupportTrait;
+
+	public const MAX_AGE = 2;
+
+	private function canBeSupportedAt(Block $block) : bool{
+		return $block->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::FARMLAND;
+	}
+
+	protected function recalculateCollisionBoxes() : array{
+		$widthTrim = $this->age === 0 ? 5 : 3;
+		$heightTrim = $this->age === 0 ? 13 : 11;
+		return [
+			AxisAlignedBB::one()
+				->trim(Facing::UP, $heightTrim / 16)
+				->squash(Axis::X, $widthTrim / 16)
+				->squash(Axis::Z, $widthTrim / 16)
+				->extend(Facing::DOWN, 1 / 16) //presumably this is to correct for farmland being 15/16 of a block tall
+		];
+	}
+
+	private function grow(?Player $player) : bool{
+		if($this->age > self::MAX_AGE){
+			return false;
+		}
+
+		if($this->age === self::MAX_AGE){
+			$up = $this->getSide(Facing::UP);
+			if($up->getTypeId() !== BlockTypeIds::AIR){
+				return false;
+			}
+
+			$tx = new BlockTransaction($this->position->getWorld());
+			$tx->addBlock($this->position, VanillaBlocks::DOUBLE_PITCHER_CROP()->setTop(false));
+			$tx->addBlock($this->position->up(), VanillaBlocks::DOUBLE_PITCHER_CROP()->setTop(true));
+
+			$ev = new StructureGrowEvent($this, $tx, $player);
+			$ev->call();
+
+			return !$ev->isCancelled() && $tx->apply();
+		}
+
+		return BlockEventHelper::grow($this, (clone $this)->setAge($this->age + 1), $player);
+	}
+
+	public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
+		if($item instanceof Fertilizer && $this->grow($player)){
+			$item->pop();
+			return true;
+		}
+
+		return false;
+	}
+
+	public function ticksRandomly() : bool{
+		return true;
+	}
+
+	public function onRandomTick() : void{
+		//TODO: the growth speed is influenced by farmland and nearby crops
+		if(mt_rand(0, 2) === 0){
+			$this->grow(null);
+		}
+	}
+}
diff --git a/src/block/PressurePlate.php b/src/block/PressurePlate.php
index d67433a750b..1dd4b50d97d 100644
--- a/src/block/PressurePlate.php
+++ b/src/block/PressurePlate.php
@@ -23,21 +23,19 @@
 
 namespace pocketmine\block;
 
+use pocketmine\block\utils\StaticSupportTrait;
 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{
+	use StaticSupportTrait;
 
 	private readonly int $deactivationDelayTicks;
 
@@ -60,24 +58,11 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
-	}
-
-	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		if($this->canBeSupportedAt($blockReplace)){
-			return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
-		}
-		return false;
+		return SupportType::NONE;
 	}
 
 	private function canBeSupportedAt(Block $block) : bool{
-		return !$block->getAdjacentSupportType(Facing::DOWN)->equals(SupportType::NONE());
-	}
-
-	public function onNearbyBlockChange() : void{
-		if(!$this->canBeSupportedAt($this)){
-			$this->position->getWorld()->useBreakOn($this->position);
-		}
+		return $block->getAdjacentSupportType(Facing::DOWN) !== SupportType::NONE;
 	}
 
 	public function hasEntityCollision() : bool{
diff --git a/src/block/RedMushroomBlock.php b/src/block/RedMushroomBlock.php
index ecac38e18c7..0a7cb30f253 100644
--- a/src/block/RedMushroomBlock.php
+++ b/src/block/RedMushroomBlock.php
@@ -29,17 +29,12 @@
 use function mt_rand;
 
 class RedMushroomBlock extends Opaque{
-	protected MushroomBlockType $mushroomBlockType;
-
-	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->mushroomBlockType = MushroomBlockType::ALL_CAP();
-		parent::__construct($idInfo, $name, $typeInfo);
-	}
+	protected MushroomBlockType $mushroomBlockType = MushroomBlockType::ALL_CAP;
 
 	public function describeBlockItemState(RuntimeDataDescriber $w) : void{
 		//these blocks always drop as all-cap, but may exist in other forms in the inventory (particularly creative),
 		//so this information needs to be kept in the type info
-		$w->mushroomBlockType($this->mushroomBlockType);
+		$w->enum($this->mushroomBlockType);
 	}
 
 	public function getMushroomBlockType() : MushroomBlockType{ return $this->mushroomBlockType; }
@@ -61,10 +56,10 @@ public function isAffectedBySilkTouch() : bool{
 	}
 
 	public function getSilkTouchDrops(Item $item) : array{
-		return [(clone $this)->setMushroomBlockType(MushroomBlockType::ALL_CAP())->asItem()];
+		return [(clone $this)->setMushroomBlockType(MushroomBlockType::ALL_CAP)->asItem()];
 	}
 
 	public function getPickedItem(bool $addUserData = false) : Item{
-		return (clone $this)->setMushroomBlockType(MushroomBlockType::ALL_CAP())->asItem();
+		return (clone $this)->setMushroomBlockType(MushroomBlockType::ALL_CAP)->asItem();
 	}
 }
diff --git a/src/block/RedstoneComparator.php b/src/block/RedstoneComparator.php
index 8b436020b77..ee63a77a99d 100644
--- a/src/block/RedstoneComparator.php
+++ b/src/block/RedstoneComparator.php
@@ -27,6 +27,7 @@
 use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait;
 use pocketmine\block\utils\HorizontalFacingTrait;
 use pocketmine\block\utils\PoweredByRedstoneTrait;
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\block\utils\SupportType;
 use pocketmine\data\runtime\RuntimeDataDescriber;
 use pocketmine\item\Item;
@@ -41,6 +42,7 @@ class RedstoneComparator extends Flowable{
 	use HorizontalFacingTrait;
 	use AnalogRedstoneSignalEmitterTrait;
 	use PoweredByRedstoneTrait;
+	use StaticSupportTrait;
 
 	protected bool $isSubtractMode = false;
 
@@ -85,14 +87,10 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		if($this->canBeSupportedAt($blockReplace)){
-			if($player !== null){
-				$this->facing = Facing::opposite($player->getHorizontalFacing());
-			}
-			return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
+		if($player !== null){
+			$this->facing = Facing::opposite($player->getHorizontalFacing());
 		}
-
-		return false;
+		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{
@@ -101,14 +99,8 @@ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player
 		return true;
 	}
 
-	public function onNearbyBlockChange() : void{
-		if(!$this->canBeSupportedAt($this)){
-			$this->position->getWorld()->useBreakOn($this->position);
-		}
-	}
-
 	private function canBeSupportedAt(Block $block) : bool{
-		return !$block->getAdjacentSupportType(Facing::DOWN)->equals(SupportType::NONE());
+		return $block->getAdjacentSupportType(Facing::DOWN) !== SupportType::NONE;
 	}
 
 	//TODO: redstone functionality
diff --git a/src/block/RedstoneOre.php b/src/block/RedstoneOre.php
index 75f5063eefa..9e537bd279a 100644
--- a/src/block/RedstoneOre.php
+++ b/src/block/RedstoneOre.php
@@ -70,7 +70,7 @@ public function onNearbyBlockChange() : void{
 	}
 
 	public function ticksRandomly() : bool{
-		return true;
+		return $this->lit;
 	}
 
 	public function onRandomTick() : void{
diff --git a/src/block/RedstoneRepeater.php b/src/block/RedstoneRepeater.php
index 518eeb9e5e2..7e6e73da846 100644
--- a/src/block/RedstoneRepeater.php
+++ b/src/block/RedstoneRepeater.php
@@ -25,6 +25,7 @@
 
 use pocketmine\block\utils\HorizontalFacingTrait;
 use pocketmine\block\utils\PoweredByRedstoneTrait;
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\block\utils\SupportType;
 use pocketmine\data\runtime\RuntimeDataDescriber;
 use pocketmine\item\Item;
@@ -37,6 +38,7 @@
 class RedstoneRepeater extends Flowable{
 	use HorizontalFacingTrait;
 	use PoweredByRedstoneTrait;
+	use StaticSupportTrait;
 
 	public const MIN_DELAY = 1;
 	public const MAX_DELAY = 4;
@@ -45,7 +47,7 @@ class RedstoneRepeater extends Flowable{
 
 	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
 		$w->horizontalFacing($this->facing);
-		$w->boundedInt(2, self::MIN_DELAY, self::MAX_DELAY, $this->delay);
+		$w->boundedIntAuto(self::MIN_DELAY, self::MAX_DELAY, $this->delay);
 		$w->bool($this->powered);
 	}
 
@@ -68,15 +70,11 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		if($this->canBeSupportedAt($blockReplace)){
-			if($player !== null){
-				$this->facing = Facing::opposite($player->getHorizontalFacing());
-			}
-
-			return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
+		if($player !== null){
+			$this->facing = Facing::opposite($player->getHorizontalFacing());
 		}
 
-		return false;
+		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{
@@ -87,14 +85,8 @@ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player
 		return true;
 	}
 
-	public function onNearbyBlockChange() : void{
-		if(!$this->canBeSupportedAt($this)){
-			$this->position->getWorld()->useBreakOn($this->position);
-		}
-	}
-
 	private function canBeSupportedAt(Block $block) : bool{
-		return !$block->getAdjacentSupportType(Facing::DOWN)->equals(SupportType::NONE());
+		return $block->getAdjacentSupportType(Facing::DOWN) !== SupportType::NONE;
 	}
 
 	//TODO: redstone functionality
diff --git a/src/block/RedstoneWire.php b/src/block/RedstoneWire.php
index 167365f56e0..a2d293fca0d 100644
--- a/src/block/RedstoneWire.php
+++ b/src/block/RedstoneWire.php
@@ -24,22 +24,14 @@
 namespace pocketmine\block;
 
 use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait;
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\item\Item;
 use pocketmine\item\VanillaItems;
 use pocketmine\math\Facing;
-use pocketmine\math\Vector3;
-use pocketmine\player\Player;
-use pocketmine\world\BlockTransaction;
 
 class RedstoneWire extends Flowable{
 	use AnalogRedstoneSignalEmitterTrait;
-
-	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		if($this->canBeSupportedAt($blockReplace)){
-			return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
-		}
-		return false;
-	}
+	use StaticSupportTrait;
 
 	public function readStateFromWorld() : Block{
 		parent::readStateFromWorld();
@@ -48,12 +40,6 @@ public function readStateFromWorld() : Block{
 		return $this;
 	}
 
-	public function onNearbyBlockChange() : void{
-		if(!$this->canBeSupportedAt($this)){
-			$this->position->getWorld()->useBreakOn($this->position);
-		}
-	}
-
 	private function canBeSupportedAt(Block $block) : bool{
 		return $block->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport();
 	}
diff --git a/src/block/Sapling.php b/src/block/Sapling.php
index b1f255f0d7e..b3fdf59af63 100644
--- a/src/block/Sapling.php
+++ b/src/block/Sapling.php
@@ -24,6 +24,7 @@
 namespace pocketmine\block;
 
 use pocketmine\block\utils\SaplingType;
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\data\runtime\RuntimeDataDescriber;
 use pocketmine\event\block\StructureGrowEvent;
 use pocketmine\item\Fertilizer;
@@ -32,11 +33,12 @@
 use pocketmine\math\Vector3;
 use pocketmine\player\Player;
 use pocketmine\utils\Random;
-use pocketmine\world\BlockTransaction;
 use pocketmine\world\generator\object\TreeFactory;
 use function mt_rand;
 
 class Sapling extends Flowable{
+	use StaticSupportTrait;
+
 	protected bool $ready = false;
 
 	private SaplingType $saplingType;
@@ -58,13 +60,9 @@ public function setReady(bool $ready) : self{
 		return $this;
 	}
 
-	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		$down = $this->getSide(Facing::DOWN);
-		if($down->hasTypeTag(BlockTypeTags::DIRT) || $down->hasTypeTag(BlockTypeTags::MUD)){
-			return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
-		}
-
-		return false;
+	private function canBeSupportedAt(Block $block) : bool{
+		$supportBlock = $block->getSide(Facing::DOWN);
+		return $supportBlock->hasTypeTag(BlockTypeTags::DIRT) || $supportBlock->hasTypeTag(BlockTypeTags::MUD);
 	}
 
 	public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
@@ -77,13 +75,6 @@ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player
 		return false;
 	}
 
-	public function onNearbyBlockChange() : void{
-		$down = $this->getSide(Facing::DOWN);
-		if(!$down->hasTypeTag(BlockTypeTags::DIRT) && !$down->hasTypeTag(BlockTypeTags::MUD)){
-			$this->position->getWorld()->useBreakOn($this->position);
-		}
-	}
-
 	public function ticksRandomly() : bool{
 		return true;
 	}
diff --git a/src/block/SeaPickle.php b/src/block/SeaPickle.php
index 4e667038e0c..627af9bacc6 100644
--- a/src/block/SeaPickle.php
+++ b/src/block/SeaPickle.php
@@ -39,7 +39,7 @@ class SeaPickle extends Transparent{
 	protected bool $underwater = false;
 
 	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
-		$w->boundedInt(2, self::MIN_COUNT, self::MAX_COUNT, $this->count);
+		$w->boundedIntAuto(self::MIN_COUNT, self::MAX_COUNT, $this->count);
 		$w->bool($this->underwater);
 	}
 
@@ -78,7 +78,7 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
diff --git a/src/block/ShulkerBox.php b/src/block/ShulkerBox.php
index 821e022f4ae..d557401eec3 100644
--- a/src/block/ShulkerBox.php
+++ b/src/block/ShulkerBox.php
@@ -113,6 +113,6 @@ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 }
diff --git a/src/block/Slab.php b/src/block/Slab.php
index cbf5dce189a..6000bec39e0 100644
--- a/src/block/Slab.php
+++ b/src/block/Slab.php
@@ -34,19 +34,18 @@
 use pocketmine\world\BlockTransaction;
 
 class Slab extends Transparent{
-	protected SlabType $slabType;
+	protected SlabType $slabType = SlabType::BOTTOM;
 
 	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->slabType = SlabType::BOTTOM();
 		parent::__construct($idInfo, $name . " Slab", $typeInfo);
 	}
 
 	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
-		$w->slabType($this->slabType);
+		$w->enum($this->slabType);
 	}
 
 	public function isTransparent() : bool{
-		return !$this->slabType->equals(SlabType::DOUBLE());
+		return $this->slabType !== SlabType::DOUBLE;
 	}
 
 	/**
@@ -69,8 +68,8 @@ public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $fa
 			return true;
 		}
 
-		if($blockReplace instanceof Slab && !$blockReplace->slabType->equals(SlabType::DOUBLE()) && $blockReplace->hasSameTypeId($this)){
-			if($blockReplace->slabType->equals(SlabType::TOP())){ //Trying to combine with top slab
+		if($blockReplace instanceof Slab && $blockReplace->slabType !== SlabType::DOUBLE && $blockReplace->hasSameTypeId($this)){
+			if($blockReplace->slabType === SlabType::TOP){ //Trying to combine with top slab
 				return $clickVector->y <= 0.5 || (!$isClickedBlock && $face === Facing::UP);
 			}else{
 				return $clickVector->y >= 0.5 || (!$isClickedBlock && $face === Facing::DOWN);
@@ -81,14 +80,14 @@ public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $fa
 	}
 
 	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		if($blockReplace instanceof Slab && !$blockReplace->slabType->equals(SlabType::DOUBLE()) && $blockReplace->hasSameTypeId($this) && (
-			($blockReplace->slabType->equals(SlabType::TOP()) && ($clickVector->y <= 0.5 || $face === Facing::UP)) ||
-			($blockReplace->slabType->equals(SlabType::BOTTOM()) && ($clickVector->y >= 0.5 || $face === Facing::DOWN))
+		if($blockReplace instanceof Slab && $blockReplace->slabType !== SlabType::DOUBLE && $blockReplace->hasSameTypeId($this) && (
+			($blockReplace->slabType === SlabType::TOP && ($clickVector->y <= 0.5 || $face === Facing::UP)) ||
+			($blockReplace->slabType === SlabType::BOTTOM && ($clickVector->y >= 0.5 || $face === Facing::DOWN))
 		)){
 			//Clicked in empty half of existing slab
-			$this->slabType = SlabType::DOUBLE();
+			$this->slabType = SlabType::DOUBLE;
 		}else{
-			$this->slabType = (($face !== Facing::UP && $clickVector->y > 0.5) || $face === Facing::DOWN) ? SlabType::TOP() : SlabType::BOTTOM();
+			$this->slabType = (($face !== Facing::UP && $clickVector->y > 0.5) || $face === Facing::DOWN) ? SlabType::TOP : SlabType::BOTTOM;
 		}
 
 		return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
@@ -98,22 +97,22 @@ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Blo
 	 * @return AxisAlignedBB[]
 	 */
 	protected function recalculateCollisionBoxes() : array{
-		if($this->slabType->equals(SlabType::DOUBLE())){
+		if($this->slabType === SlabType::DOUBLE){
 			return [AxisAlignedBB::one()];
 		}
-		return [AxisAlignedBB::one()->trim($this->slabType->equals(SlabType::TOP()) ? Facing::DOWN : Facing::UP, 0.5)];
+		return [AxisAlignedBB::one()->trim($this->slabType === SlabType::TOP ? Facing::DOWN : Facing::UP, 0.5)];
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		if($this->slabType->equals(SlabType::DOUBLE())){
-			return SupportType::FULL();
-		}elseif(($facing === Facing::UP && $this->slabType->equals(SlabType::TOP())) || ($facing === Facing::DOWN && $this->slabType->equals(SlabType::BOTTOM()))){
-			return SupportType::FULL();
+		if($this->slabType === SlabType::DOUBLE){
+			return SupportType::FULL;
+		}elseif(($facing === Facing::UP && $this->slabType === SlabType::TOP) || ($facing === Facing::DOWN && $this->slabType === SlabType::BOTTOM)){
+			return SupportType::FULL;
 		}
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	public function getDropsForCompatibleTool(Item $item) : array{
-		return [$this->asItem()->setCount($this->slabType->equals(SlabType::DOUBLE()) ? 2 : 1)];
+		return [$this->asItem()->setCount($this->slabType === SlabType::DOUBLE ? 2 : 1)];
 	}
 }
diff --git a/src/block/SmallDripleaf.php b/src/block/SmallDripleaf.php
index e08e6f6e98c..ad23608bf1a 100644
--- a/src/block/SmallDripleaf.php
+++ b/src/block/SmallDripleaf.php
@@ -161,7 +161,7 @@ public function getFlammability() : int{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	protected function recalculateCollisionBoxes() : array{
diff --git a/src/block/SnowLayer.php b/src/block/SnowLayer.php
index 05fc8842131..cca8424a983 100644
--- a/src/block/SnowLayer.php
+++ b/src/block/SnowLayer.php
@@ -47,7 +47,7 @@ class SnowLayer extends Flowable implements Fallable{
 	protected int $layers = self::MIN_LAYERS;
 
 	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
-		$w->boundedInt(3, self::MIN_LAYERS, self::MAX_LAYERS, $this->layers);
+		$w->boundedIntAuto(self::MIN_LAYERS, self::MAX_LAYERS, $this->layers);
 	}
 
 	public function getLayers() : int{ return $this->layers; }
@@ -75,13 +75,13 @@ protected function recalculateCollisionBoxes() : array{
 
 	public function getSupportType(int $facing) : SupportType{
 		if(!$this->canBeReplaced()){
-			return SupportType::FULL();
+			return SupportType::FULL;
 		}
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	private function canBeSupportedAt(Block $block) : bool{
-		return $block->getAdjacentSupportType(Facing::DOWN)->equals(SupportType::FULL());
+		return $block->getAdjacentSupportType(Facing::DOWN) === SupportType::FULL;
 	}
 
 	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
diff --git a/src/block/SporeBlossom.php b/src/block/SporeBlossom.php
index 9099321780c..3ca5fa1f206 100644
--- a/src/block/SporeBlossom.php
+++ b/src/block/SporeBlossom.php
@@ -23,30 +23,14 @@
 
 namespace pocketmine\block;
 
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\block\utils\SupportType;
-use pocketmine\item\Item;
 use pocketmine\math\Facing;
-use pocketmine\math\Vector3;
-use pocketmine\player\Player;
-use pocketmine\world\BlockTransaction;
 
 final class SporeBlossom extends Flowable{
+	use StaticSupportTrait;
 
 	private function canBeSupportedAt(Block $block) : bool{
-		return $block->getAdjacentSupportType(Facing::UP)->equals(SupportType::FULL());
-	}
-
-	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		if(!$this->canBeSupportedAt($blockReplace)){
-			return false;
-		}
-
-		return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
-	}
-
-	public function onNearbyBlockChange() : void{
-		if(!$this->canBeSupportedAt($this)){
-			$this->position->getWorld()->useBreakOn($this->position);
-		}
+		return $block->getAdjacentSupportType(Facing::UP) === SupportType::FULL;
 	}
 }
diff --git a/src/block/StainedGlass.php b/src/block/StainedGlass.php
index 5b4b6a883c3..bc0d8487700 100644
--- a/src/block/StainedGlass.php
+++ b/src/block/StainedGlass.php
@@ -24,13 +24,7 @@
 namespace pocketmine\block;
 
 use pocketmine\block\utils\ColoredTrait;
-use pocketmine\block\utils\DyeColor;
 
 final class StainedGlass extends Glass{
 	use ColoredTrait;
-
-	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->color = DyeColor::WHITE();
-		parent::__construct($idInfo, $name, $typeInfo);
-	}
 }
diff --git a/src/block/StainedGlassPane.php b/src/block/StainedGlassPane.php
index 2a592395dde..18ecfdee0e6 100644
--- a/src/block/StainedGlassPane.php
+++ b/src/block/StainedGlassPane.php
@@ -24,13 +24,7 @@
 namespace pocketmine\block;
 
 use pocketmine\block\utils\ColoredTrait;
-use pocketmine\block\utils\DyeColor;
 
 final class StainedGlassPane extends GlassPane{
 	use ColoredTrait;
-
-	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->color = DyeColor::WHITE();
-		parent::__construct($idInfo, $name, $typeInfo);
-	}
 }
diff --git a/src/block/StainedHardenedClay.php b/src/block/StainedHardenedClay.php
index 1a9d68737be..2c2c01ba3ab 100644
--- a/src/block/StainedHardenedClay.php
+++ b/src/block/StainedHardenedClay.php
@@ -24,13 +24,7 @@
 namespace pocketmine\block;
 
 use pocketmine\block\utils\ColoredTrait;
-use pocketmine\block\utils\DyeColor;
 
 final class StainedHardenedClay extends HardenedClay{
 	use ColoredTrait;
-
-	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->color = DyeColor::WHITE();
-		parent::__construct($idInfo, $name, $typeInfo);
-	}
 }
diff --git a/src/block/StainedHardenedGlass.php b/src/block/StainedHardenedGlass.php
index 85d5fcec0f7..cc609a49a9b 100644
--- a/src/block/StainedHardenedGlass.php
+++ b/src/block/StainedHardenedGlass.php
@@ -24,13 +24,7 @@
 namespace pocketmine\block;
 
 use pocketmine\block\utils\ColoredTrait;
-use pocketmine\block\utils\DyeColor;
 
 final class StainedHardenedGlass extends HardenedGlass{
 	use ColoredTrait;
-
-	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->color = DyeColor::WHITE();
-		parent::__construct($idInfo, $name, $typeInfo);
-	}
 }
diff --git a/src/block/StainedHardenedGlassPane.php b/src/block/StainedHardenedGlassPane.php
index 4a61f9778bc..63dbe1f776e 100644
--- a/src/block/StainedHardenedGlassPane.php
+++ b/src/block/StainedHardenedGlassPane.php
@@ -24,13 +24,7 @@
 namespace pocketmine\block;
 
 use pocketmine\block\utils\ColoredTrait;
-use pocketmine\block\utils\DyeColor;
 
 final class StainedHardenedGlassPane extends HardenedGlassPane{
 	use ColoredTrait;
-
-	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->color = DyeColor::WHITE();
-		parent::__construct($idInfo, $name, $typeInfo);
-	}
 }
diff --git a/src/block/Stair.php b/src/block/Stair.php
index a2074672170..25d1da8d99f 100644
--- a/src/block/Stair.php
+++ b/src/block/Stair.php
@@ -39,12 +39,7 @@ class Stair extends Transparent{
 	use HorizontalFacingTrait;
 
 	protected bool $upsideDown = false;
-	protected StairShape $shape;
-
-	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->shape = StairShape::STRAIGHT();
-		parent::__construct($idInfo, $name, $typeInfo);
-	}
+	protected StairShape $shape = StairShape::STRAIGHT;
 
 	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
 		$w->horizontalFacing($this->facing);
@@ -56,11 +51,11 @@ public function readStateFromWorld() : Block{
 
 		$clockwise = Facing::rotateY($this->facing, true);
 		if(($backFacing = $this->getPossibleCornerFacing(false)) !== null){
-			$this->shape = $backFacing === $clockwise ? StairShape::OUTER_RIGHT() : StairShape::OUTER_LEFT();
+			$this->shape = $backFacing === $clockwise ? StairShape::OUTER_RIGHT : StairShape::OUTER_LEFT;
 		}elseif(($frontFacing = $this->getPossibleCornerFacing(true)) !== null){
-			$this->shape = $frontFacing === $clockwise ? StairShape::INNER_RIGHT() : StairShape::INNER_LEFT();
+			$this->shape = $frontFacing === $clockwise ? StairShape::INNER_RIGHT : StairShape::INNER_LEFT;
 		}else{
-			$this->shape = StairShape::STRAIGHT();
+			$this->shape = StairShape::STRAIGHT;
 		}
 
 		return $this;
@@ -92,14 +87,14 @@ protected function recalculateCollisionBoxes() : array{
 			->trim(Facing::opposite($topStepFace), 0.5)
 			->trim(Facing::opposite($this->facing), 0.5);
 
-		if($this->shape->equals(StairShape::OUTER_LEFT()) || $this->shape->equals(StairShape::OUTER_RIGHT())){
-			$topStep->trim(Facing::rotateY($this->facing, $this->shape->equals(StairShape::OUTER_LEFT())), 0.5);
-		}elseif($this->shape->equals(StairShape::INNER_LEFT()) || $this->shape->equals(StairShape::INNER_RIGHT())){
+		if($this->shape === StairShape::OUTER_LEFT || $this->shape === StairShape::OUTER_RIGHT){
+			$topStep->trim(Facing::rotateY($this->facing, $this->shape === StairShape::OUTER_LEFT), 0.5);
+		}elseif($this->shape === StairShape::INNER_LEFT || $this->shape === StairShape::INNER_RIGHT){
 			//add an extra cube
 			$bbs[] = AxisAlignedBB::one()
 				->trim(Facing::opposite($topStepFace), 0.5)
 				->trim($this->facing, 0.5) //avoid overlapping with main step
-				->trim(Facing::rotateY($this->facing, $this->shape->equals(StairShape::INNER_LEFT())), 0.5);
+				->trim(Facing::rotateY($this->facing, $this->shape === StairShape::INNER_LEFT), 0.5);
 		}
 
 		$bbs[] = $topStep;
@@ -111,13 +106,13 @@ public function getSupportType(int $facing) : SupportType{
 		if(
 			$facing === Facing::UP && $this->isUpsideDown() ||
 			$facing === Facing::DOWN && !$this->isUpsideDown() ||
-			($facing === $this->facing && !$this->shape->equals(StairShape::OUTER_LEFT()) && !$this->shape->equals(StairShape::OUTER_RIGHT())) ||
-			($facing === Facing::rotate($this->facing, Axis::Y, false) && $this->shape->equals(StairShape::INNER_LEFT())) ||
-			($facing === Facing::rotate($this->facing, Axis::Y, true) && $this->shape->equals(StairShape::INNER_RIGHT()))
+			($facing === $this->facing && $this->shape !== StairShape::OUTER_LEFT && $this->shape !== StairShape::OUTER_RIGHT) ||
+			($facing === Facing::rotate($this->facing, Axis::Y, false) && $this->shape === StairShape::INNER_LEFT) ||
+			($facing === Facing::rotate($this->facing, Axis::Y, true) && $this->shape === StairShape::INNER_RIGHT)
 		){
-			return SupportType::FULL();
+			return SupportType::FULL;
 		}
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	private function getPossibleCornerFacing(bool $oppositeFacing) : ?int{
diff --git a/src/block/Stonecutter.php b/src/block/Stonecutter.php
index eb7dc68c26c..30c19d25dcb 100644
--- a/src/block/Stonecutter.php
+++ b/src/block/Stonecutter.php
@@ -47,6 +47,6 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 }
diff --git a/src/block/Sugarcane.php b/src/block/Sugarcane.php
index 3757e457725..97b4aee9c19 100644
--- a/src/block/Sugarcane.php
+++ b/src/block/Sugarcane.php
@@ -23,8 +23,9 @@
 
 namespace pocketmine\block;
 
+use pocketmine\block\utils\AgeableTrait;
 use pocketmine\block\utils\BlockEventHelper;
-use pocketmine\data\runtime\RuntimeDataDescriber;
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\item\Fertilizer;
 use pocketmine\item\Item;
 use pocketmine\math\Facing;
@@ -34,13 +35,10 @@
 use pocketmine\world\Position;
 
 class Sugarcane extends Flowable{
-	public const MAX_AGE = 15;
-
-	protected int $age = 0;
+	use AgeableTrait;
+	use StaticSupportTrait;
 
-	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
-		$w->boundedInt(4, 0, self::MAX_AGE, $this->age);
-	}
+	public const MAX_AGE = 15;
 
 	private function seekToBottom() : Position{
 		$world = $this->position->getWorld();
@@ -74,17 +72,6 @@ private function grow(Position $pos, ?Player $player = null) : bool{
 		return $grew;
 	}
 
-	public function getAge() : int{ return $this->age; }
-
-	/** @return $this */
-	public function setAge(int $age) : self{
-		if($age < 0 || $age > self::MAX_AGE){
-			throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
-		}
-		$this->age = $age;
-		return $this;
-	}
-
 	public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
 		if($item instanceof Fertilizer){
 			if($this->grow($this->seekToBottom(), $player)){
@@ -97,18 +84,12 @@ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player
 		return false;
 	}
 
-	private function canBeSupportedBy(Block $block) : bool{
-		return
-			$block->hasTypeTag(BlockTypeTags::MUD) ||
-			$block->hasTypeTag(BlockTypeTags::DIRT) ||
-			$block->hasTypeTag(BlockTypeTags::SAND);
-	}
-
-	public function onNearbyBlockChange() : void{
-		$down = $this->getSide(Facing::DOWN);
-		if(!$down->hasSameTypeId($this) && !$this->canBeSupportedBy($down)){
-			$this->position->getWorld()->useBreakOn($this->position);
-		}
+	private function canBeSupportedAt(Block $block) : bool{
+		$supportBlock = $block->getSide(Facing::DOWN);
+		return $supportBlock->hasSameTypeId($this) ||
+			$supportBlock->hasTypeTag(BlockTypeTags::MUD) ||
+			$supportBlock->hasTypeTag(BlockTypeTags::DIRT) ||
+			$supportBlock->hasTypeTag(BlockTypeTags::SAND);
 	}
 
 	public function ticksRandomly() : bool{
@@ -127,15 +108,16 @@ public function onRandomTick() : void{
 	}
 
 	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		$down = $this->getSide(Facing::DOWN);
+		$down = $blockReplace->getSide(Facing::DOWN);
 		if($down->hasSameTypeId($this)){
 			return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
-		}elseif($this->canBeSupportedBy($down)){
-			foreach(Facing::HORIZONTAL as $side){
-				$sideBlock = $down->getSide($side);
-				if($sideBlock instanceof Water || $sideBlock instanceof FrostedIce){
-					return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
-				}
+		}
+
+		//support criteria are checked by FixedSupportTrait, but this part applies to placement only
+		foreach(Facing::HORIZONTAL as $side){
+			$sideBlock = $down->getSide($side);
+			if($sideBlock instanceof Water || $sideBlock instanceof FrostedIce){
+				return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
 			}
 		}
 
diff --git a/src/block/SweetBerryBush.php b/src/block/SweetBerryBush.php
index ef1169df531..a0a26ef6e3d 100644
--- a/src/block/SweetBerryBush.php
+++ b/src/block/SweetBerryBush.php
@@ -23,9 +23,10 @@
 
 namespace pocketmine\block;
 
+use pocketmine\block\utils\AgeableTrait;
 use pocketmine\block\utils\BlockEventHelper;
 use pocketmine\block\utils\FortuneDropHelper;
-use pocketmine\data\runtime\RuntimeDataDescriber;
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\entity\Entity;
 use pocketmine\entity\Living;
 use pocketmine\event\entity\EntityDamageByBlockEvent;
@@ -35,31 +36,17 @@
 use pocketmine\math\Facing;
 use pocketmine\math\Vector3;
 use pocketmine\player\Player;
-use pocketmine\world\BlockTransaction;
 use function mt_rand;
 
 class SweetBerryBush extends Flowable{
+	use AgeableTrait;
+	use StaticSupportTrait;
+
 	public const STAGE_SAPLING = 0;
 	public const STAGE_BUSH_NO_BERRIES = 1;
 	public const STAGE_BUSH_SOME_BERRIES = 2;
 	public const STAGE_MATURE = 3;
-
-	protected int $age = self::STAGE_SAPLING;
-
-	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
-		$w->boundedInt(3, self::STAGE_SAPLING, self::STAGE_MATURE, $this->age);
-	}
-
-	public function getAge() : int{ return $this->age; }
-
-	/** @return $this */
-	public function setAge(int $age) : self{
-		if($age < self::STAGE_SAPLING || $age > self::STAGE_MATURE){
-			throw new \InvalidArgumentException("Age must be in range 0-3");
-		}
-		$this->age = $age;
-		return $this;
-	}
+	public const MAX_AGE = self::STAGE_MATURE;
 
 	public function getBerryDropAmount() : int{
 		if($this->age === self::STAGE_MATURE){
@@ -70,16 +57,17 @@ public function getBerryDropAmount() : int{
 		return 0;
 	}
 
+	/**
+	 * @deprecated
+	 */
 	protected function canBeSupportedBy(Block $block) : bool{
 		return $block->getTypeId() !== BlockTypeIds::FARMLAND && //bedrock-specific thing (bug?)
 			($block->hasTypeTag(BlockTypeTags::DIRT) || $block->hasTypeTag(BlockTypeTags::MUD));
 	}
 
-	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		if(!$this->canBeSupportedBy($blockReplace->getSide(Facing::DOWN))){
-			return false;
-		}
-		return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
+	private function canBeSupportedAt(Block $block) : bool{
+		$supportBlock = $block->getSide(Facing::DOWN);
+		return $this->canBeSupportedBy($supportBlock);
 	}
 
 	public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
@@ -113,14 +101,8 @@ public function getDropsForCompatibleTool(Item $item) : array{
 		];
 	}
 
-	public function onNearbyBlockChange() : void{
-		if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
-			$this->position->getWorld()->useBreakOn($this->position);
-		}
-	}
-
 	public function ticksRandomly() : bool{
-		return true;
+		return $this->age < self::STAGE_MATURE;
 	}
 
 	public function onRandomTick() : void{
diff --git a/src/block/TallGrass.php b/src/block/TallGrass.php
index d8c34b00108..459751c4e9c 100644
--- a/src/block/TallGrass.php
+++ b/src/block/TallGrass.php
@@ -23,31 +23,16 @@
 
 namespace pocketmine\block;
 
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\block\utils\TallGrassTrait;
-use pocketmine\item\Item;
 use pocketmine\math\Facing;
-use pocketmine\math\Vector3;
-use pocketmine\player\Player;
-use pocketmine\world\BlockTransaction;
 
 class TallGrass extends Flowable{
 	use TallGrassTrait;
+	use StaticSupportTrait;
 
-	private function canBeSupportedBy(Block $block) : bool{
-		return $block->hasTypeTag(BlockTypeTags::DIRT) || $block->hasTypeTag(BlockTypeTags::MUD);
-	}
-
-	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		if($this->canBeSupportedBy($this->getSide(Facing::DOWN))){
-			return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
-		}
-
-		return false;
-	}
-
-	public function onNearbyBlockChange() : void{
-		if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){ //Replace with common break method
-			$this->position->getWorld()->useBreakOn($this->position);
-		}
+	private function canBeSupportedAt(Block $block) : bool{
+		$supportBlock = $block->getSide(Facing::DOWN);
+		return $supportBlock->hasTypeTag(BlockTypeTags::DIRT) || $supportBlock->hasTypeTag(BlockTypeTags::MUD);
 	}
 }
diff --git a/src/block/Thin.php b/src/block/Thin.php
index 2b89b4beed6..bf82c3e584f 100644
--- a/src/block/Thin.php
+++ b/src/block/Thin.php
@@ -41,7 +41,7 @@ public function readStateFromWorld() : Block{
 
 		foreach(Facing::HORIZONTAL as $facing){
 			$side = $this->getSide($facing);
-			if($side instanceof Thin || $side instanceof Wall || $side->getSupportType(Facing::opposite($facing))->equals(SupportType::FULL())){
+			if($side instanceof Thin || $side instanceof Wall || $side->getSupportType(Facing::opposite($facing)) === SupportType::FULL){
 				$this->connections[$facing] = true;
 			}else{
 				unset($this->connections[$facing]);
@@ -90,6 +90,6 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 }
diff --git a/src/block/Torch.php b/src/block/Torch.php
index 66b62bc19ae..aee4da32aa4 100644
--- a/src/block/Torch.php
+++ b/src/block/Torch.php
@@ -84,6 +84,6 @@ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Blo
 	private function canBeSupportedAt(Block $block, int $face) : bool{
 		return $face === Facing::DOWN ?
 			$block->getAdjacentSupportType($face)->hasCenterSupport() :
-			$block->getAdjacentSupportType($face)->equals(SupportType::FULL());
+			$block->getAdjacentSupportType($face) === SupportType::FULL;
 	}
 }
diff --git a/src/block/TorchflowerCrop.php b/src/block/TorchflowerCrop.php
new file mode 100644
index 00000000000..75efe142b28
--- /dev/null
+++ b/src/block/TorchflowerCrop.php
@@ -0,0 +1,90 @@
+<?php
+
+/*
+ *
+ *  ____            _        _   __  __ _                  __  __ ____
+ * |  _ \ ___   ___| | _____| |_|  \/  (_)_ __   ___      |  \/  |  _ \
+ * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
+ * |  __/ (_) | (__|   <  __/ |_| |  | | | | | |  __/_____| |  | |  __/
+ * |_|   \___/ \___|_|\_\___|\__|_|  |_|_|_| |_|\___|     |_|  |_|_|
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * @author PocketMine Team
+ * @link http://www.pocketmine.net/
+ *
+ *
+ */
+
+declare(strict_types=1);
+
+namespace pocketmine\block;
+
+use pocketmine\block\utils\BlockEventHelper;
+use pocketmine\block\utils\StaticSupportTrait;
+use pocketmine\data\runtime\RuntimeDataDescriber;
+use pocketmine\item\Fertilizer;
+use pocketmine\item\Item;
+use pocketmine\item\VanillaItems;
+use pocketmine\math\Facing;
+use pocketmine\math\Vector3;
+use pocketmine\player\Player;
+use function mt_rand;
+
+final class TorchflowerCrop extends Flowable{
+	use StaticSupportTrait;
+
+	private bool $ready = false;
+
+	public function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
+		$w->bool($this->ready);
+	}
+
+	public function isReady() : bool{ return $this->ready; }
+
+	public function setReady(bool $ready) : self{
+		$this->ready = $ready;
+		return $this;
+	}
+
+	private function canBeSupportedAt(Block $block) : bool{
+		return $block->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::FARMLAND;
+	}
+
+	private function getNextState() : Block{
+		if($this->ready){
+			return VanillaBlocks::TORCHFLOWER();
+		}else{
+			return VanillaBlocks::TORCHFLOWER_CROP()->setReady(true);
+		}
+	}
+
+	public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
+		if($item instanceof Fertilizer){
+			if(BlockEventHelper::grow($this, $this->getNextState(), $player)){
+				$item->pop();
+			}
+
+			return true;
+		}
+
+		return false;
+	}
+
+	public function ticksRandomly() : bool{
+		return true;
+	}
+
+	public function onRandomTick() : void{
+		if(mt_rand(0, 2) === 1){
+			BlockEventHelper::grow($this, $this->getNextState(), null);
+		}
+	}
+
+	public function asItem() : Item{
+		return VanillaItems::TORCHFLOWER_SEEDS();
+	}
+}
diff --git a/src/block/Trapdoor.php b/src/block/Trapdoor.php
index d12a922df62..20b6af2abdc 100644
--- a/src/block/Trapdoor.php
+++ b/src/block/Trapdoor.php
@@ -70,7 +70,7 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return SupportType::NONE();
+		return SupportType::NONE;
 	}
 
 	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php
index a41c3985b69..8349f70c066 100644
--- a/src/block/VanillaBlocks.php
+++ b/src/block/VanillaBlocks.php
@@ -38,6 +38,7 @@
 use pocketmine\block\tile\BrewingStand as TileBrewingStand;
 use pocketmine\block\tile\Cauldron as TileCauldron;
 use pocketmine\block\tile\Chest as TileChest;
+use pocketmine\block\tile\ChiseledBookshelf as TileChiseledBookshelf;
 use pocketmine\block\tile\Comparator as TileComparator;
 use pocketmine\block\tile\DaylightSensor as TileDaylightSensor;
 use pocketmine\block\tile\EnchantTable as TileEnchantingTable;
@@ -54,20 +55,18 @@
 use pocketmine\block\tile\Note as TileNote;
 use pocketmine\block\tile\ShulkerBox as TileShulkerBox;
 use pocketmine\block\tile\Smoker as TileSmoker;
+use pocketmine\block\utils\AmethystTrait;
 use pocketmine\block\utils\LeavesType;
 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;
-use pocketmine\math\RayTraceResult;
 use pocketmine\utils\CloningRegistryTrait;
-use pocketmine\world\sound\AmethystBlockChimeSound;
-use pocketmine\world\sound\BlockPunchSound;
 use function mb_strtolower;
+use function strtolower;
 
 /**
  * This doc-block is generated automatically, do not modify it manually.
@@ -95,6 +94,7 @@
  * @method static Flower ALLIUM()
  * @method static MushroomStem ALL_SIDED_MUSHROOM_STEM()
  * @method static Opaque AMETHYST()
+ * @method static AmethystCluster AMETHYST_CLUSTER()
  * @method static Opaque ANCIENT_DEBRIS()
  * @method static Opaque ANDESITE()
  * @method static Slab ANDESITE_SLAB()
@@ -148,6 +148,7 @@
  * @method static Wall BRICK_WALL()
  * @method static BrownMushroom BROWN_MUSHROOM()
  * @method static BrownMushroomBlock BROWN_MUSHROOM_BLOCK()
+ * @method static BuddingAmethyst BUDDING_AMETHYST()
  * @method static Cactus CACTUS()
  * @method static Cake CAKE()
  * @method static CakeWithCandle CAKE_WITH_CANDLE()
@@ -177,6 +178,7 @@
  * @method static WallSign CHERRY_WALL_SIGN()
  * @method static Wood CHERRY_WOOD()
  * @method static Chest CHEST()
+ * @method static ChiseledBookshelf CHISELED_BOOKSHELF()
  * @method static Opaque CHISELED_DEEPSLATE()
  * @method static Opaque CHISELED_NETHER_BRICKS()
  * @method static Opaque CHISELED_POLISHED_BLACKSTONE()
@@ -221,6 +223,7 @@
  * @method static Wood CRIMSON_HYPHAE()
  * @method static Planks CRIMSON_PLANKS()
  * @method static WoodenPressurePlate CRIMSON_PRESSURE_PLATE()
+ * @method static NetherRoots CRIMSON_ROOTS()
  * @method static FloorSign CRIMSON_SIGN()
  * @method static WoodenSlab CRIMSON_SLAB()
  * @method static WoodenStairs CRIMSON_STAIRS()
@@ -281,6 +284,7 @@
  * @method static Stair DIORITE_STAIRS()
  * @method static Wall DIORITE_WALL()
  * @method static Dirt DIRT()
+ * @method static DoublePitcherCrop DOUBLE_PITCHER_CROP()
  * @method static DoubleTallGrass DOUBLE_TALLGRASS()
  * @method static DragonEgg DRAGON_EGG()
  * @method static DriedKelp DRIED_KELP()
@@ -576,6 +580,8 @@
  * @method static DoublePlant PEONY()
  * @method static PinkPetals PINK_PETALS()
  * @method static Flower PINK_TULIP()
+ * @method static PitcherCrop PITCHER_CROP()
+ * @method static DoublePlant PITCHER_PLANT()
  * @method static Podzol PODZOL()
  * @method static Opaque POLISHED_ANDESITE()
  * @method static Slab POLISHED_ANDESITE_SLAB()
@@ -723,6 +729,8 @@
  * @method static TintedGlass TINTED_GLASS()
  * @method static TNT TNT()
  * @method static Torch TORCH()
+ * @method static Flower TORCHFLOWER()
+ * @method static TorchflowerCrop TORCHFLOWER_CROP()
  * @method static TrappedChest TRAPPED_CHEST()
  * @method static Tripwire TRIPWIRE()
  * @method static TripwireHook TRIPWIRE_HOOK()
@@ -739,6 +747,7 @@
  * @method static Wood WARPED_HYPHAE()
  * @method static Planks WARPED_PLANKS()
  * @method static WoodenPressurePlate WARPED_PRESSURE_PLATE()
+ * @method static NetherRoots WARPED_ROOTS()
  * @method static FloorSign WARPED_SIGN()
  * @method static WoodenSlab WARPED_SLAB()
  * @method static WoodenStairs WARPED_STAIRS()
@@ -782,7 +791,7 @@ protected static function setup() : void{
 		$railBreakInfo = new Info(new BlockBreakInfo(0.7));
 		self::register("activator_rail", new ActivatorRail(new BID(Ids::ACTIVATOR_RAIL), "Activator Rail", $railBreakInfo));
 		self::register("air", new Air(new BID(Ids::AIR), "Air", new Info(BreakInfo::indestructible(-1.0))));
-		self::register("anvil", new Anvil(new BID(Ids::ANVIL), "Anvil", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD(), 6000.0))));
+		self::register("anvil", new Anvil(new BID(Ids::ANVIL), "Anvil", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 6000.0))));
 		self::register("bamboo", new Bamboo(new BID(Ids::BAMBOO), "Bamboo", new Info(new class(2.0 /* 1.0 in PC */, ToolType::AXE) extends BreakInfo{
 			public function getBreakTime(Item $item) : float{
 				if($item->getBlockToolType() === ToolType::SWORD){
@@ -803,13 +812,14 @@ public function getBreakTime(Item $item) : float{
 		self::register("bedrock", new Bedrock(new BID(Ids::BEDROCK), "Bedrock", new Info(BreakInfo::indestructible())));
 
 		self::register("beetroots", new Beetroot(new BID(Ids::BEETROOTS), "Beetroot Block", new Info(BreakInfo::instant())));
-		self::register("bell", new Bell(new BID(Ids::BELL, TileBell::class), "Bell", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD()))));
+		self::register("bell", new Bell(new BID(Ids::BELL, TileBell::class), "Bell", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD))));
 		self::register("blue_ice", new BlueIce(new BID(Ids::BLUE_ICE), "Blue Ice", new Info(BreakInfo::pickaxe(2.8))));
-		self::register("bone_block", new BoneBlock(new BID(Ids::BONE_BLOCK), "Bone Block", new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD()))));
+		self::register("bone_block", new BoneBlock(new BID(Ids::BONE_BLOCK), "Bone Block", new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD))));
 		self::register("bookshelf", new Bookshelf(new BID(Ids::BOOKSHELF), "Bookshelf", new Info(BreakInfo::axe(1.5))));
-		self::register("brewing_stand", new BrewingStand(new BID(Ids::BREWING_STAND, TileBrewingStand::class), "Brewing Stand", new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD()))));
+		self::register("chiseled_bookshelf", new ChiseledBookshelf(new BID(Ids::CHISELED_BOOKSHELF, TileChiseledBookshelf::class), "Chiseled Bookshelf", new Info(BreakInfo::axe(1.5))));
+		self::register("brewing_stand", new BrewingStand(new BID(Ids::BREWING_STAND, TileBrewingStand::class), "Brewing Stand", new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD))));
 
-		$bricksBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD(), 30.0));
+		$bricksBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0));
 		self::register("brick_stairs", new Stair(new BID(Ids::BRICK_STAIRS), "Brick Stairs", $bricksBreakInfo));
 		self::register("bricks", new Opaque(new BID(Ids::BRICKS), "Bricks", $bricksBreakInfo));
 
@@ -821,9 +831,9 @@ public function getBreakTime(Item $item) : float{
 		$chestBreakInfo = new Info(BreakInfo::axe(2.5));
 		self::register("chest", new Chest(new BID(Ids::CHEST, TileChest::class), "Chest", $chestBreakInfo));
 		self::register("clay", new Clay(new BID(Ids::CLAY), "Clay Block", new Info(BreakInfo::shovel(0.6))));
-		self::register("coal", new Coal(new BID(Ids::COAL), "Coal Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD(), 30.0))));
+		self::register("coal", new Coal(new BID(Ids::COAL), "Coal Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 30.0))));
 
-		$cobblestoneBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD(), 30.0));
+		$cobblestoneBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0));
 		self::register("cobblestone", $cobblestone = new Opaque(new BID(Ids::COBBLESTONE), "Cobblestone", $cobblestoneBreakInfo));
 		self::register("mossy_cobblestone", new Opaque(new BID(Ids::MOSSY_COBBLESTONE), "Mossy Cobblestone", $cobblestoneBreakInfo));
 		self::register("cobblestone_stairs", new Stair(new BID(Ids::COBBLESTONE_STAIRS), "Cobblestone Stairs", $cobblestoneBreakInfo));
@@ -831,12 +841,12 @@ public function getBreakTime(Item $item) : float{
 
 		self::register("cobweb", new Cobweb(new BID(Ids::COBWEB), "Cobweb", new Info(new BreakInfo(4.0, ToolType::SWORD | ToolType::SHEARS, 1))));
 		self::register("cocoa_pod", new CocoaBlock(new BID(Ids::COCOA_POD), "Cocoa Block", new Info(BreakInfo::axe(0.2, null, 15.0))));
-		self::register("coral_block", new CoralBlock(new BID(Ids::CORAL_BLOCK), "Coral Block", new Info(BreakInfo::pickaxe(7.0, ToolTier::WOOD()))));
+		self::register("coral_block", new CoralBlock(new BID(Ids::CORAL_BLOCK), "Coral Block", new Info(BreakInfo::pickaxe(7.0, ToolTier::WOOD))));
 		self::register("daylight_sensor", new DaylightSensor(new BID(Ids::DAYLIGHT_SENSOR, TileDaylightSensor::class), "Daylight Sensor", new Info(BreakInfo::axe(0.2))));
 		self::register("dead_bush", new DeadBush(new BID(Ids::DEAD_BUSH), "Dead Bush", new Info(BreakInfo::instant(ToolType::SHEARS, 1), [Tags::POTTABLE_PLANTS])));
 		self::register("detector_rail", new DetectorRail(new BID(Ids::DETECTOR_RAIL), "Detector Rail", $railBreakInfo));
 
-		self::register("diamond", new Opaque(new BID(Ids::DIAMOND), "Diamond Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::IRON(), 30.0))));
+		self::register("diamond", new Opaque(new BID(Ids::DIAMOND), "Diamond Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::IRON, 30.0))));
 		self::register("dirt", new Dirt(new BID(Ids::DIRT), "Dirt", new Info(BreakInfo::shovel(0.5), [Tags::DIRT])));
 		self::register("sunflower", new DoublePlant(new BID(Ids::SUNFLOWER), "Sunflower", new Info(BreakInfo::instant())));
 		self::register("lilac", new DoublePlant(new BID(Ids::LILAC), "Lilac", new Info(BreakInfo::instant())));
@@ -845,19 +855,22 @@ public function getBreakTime(Item $item) : float{
 		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()))));
+		self::register("pitcher_plant", new DoublePlant(new BID(Ids::PITCHER_PLANT), "Pitcher Plant", new Info(BreakInfo::instant())));
+		self::register("pitcher_crop", new PitcherCrop(new BID(Ids::PITCHER_CROP), "Pitcher Crop", new Info(BreakInfo::instant())));
+		self::register("double_pitcher_crop", new DoublePitcherCrop(new BID(Ids::DOUBLE_PITCHER_CROP), "Double Pitcher Crop", new Info(BreakInfo::instant())));
+		self::register("dragon_egg", new DragonEgg(new BID(Ids::DRAGON_EGG), "Dragon Egg", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD))));
 		self::register("dried_kelp", new DriedKelp(new BID(Ids::DRIED_KELP), "Dried Kelp Block", new Info(new BreakInfo(0.5, ToolType::NONE, 0, 12.5))));
-		self::register("emerald", new Opaque(new BID(Ids::EMERALD), "Emerald Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::IRON(), 30.0))));
-		self::register("enchanting_table", new EnchantingTable(new BID(Ids::ENCHANTING_TABLE, TileEnchantingTable::class), "Enchanting Table", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD(), 6000.0))));
+		self::register("emerald", new Opaque(new BID(Ids::EMERALD), "Emerald Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::IRON, 30.0))));
+		self::register("enchanting_table", new EnchantingTable(new BID(Ids::ENCHANTING_TABLE, TileEnchantingTable::class), "Enchanting Table", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 6000.0))));
 		self::register("end_portal_frame", new EndPortalFrame(new BID(Ids::END_PORTAL_FRAME), "End Portal Frame", new Info(BreakInfo::indestructible())));
 		self::register("end_rod", new EndRod(new BID(Ids::END_ROD), "End Rod", new Info(BreakInfo::instant())));
-		self::register("end_stone", new Opaque(new BID(Ids::END_STONE), "End Stone", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD(), 45.0))));
+		self::register("end_stone", new Opaque(new BID(Ids::END_STONE), "End Stone", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD, 45.0))));
 
-		$endBrickBreakInfo = new Info(BreakInfo::pickaxe(0.8, ToolTier::WOOD(), 4.0));
+		$endBrickBreakInfo = new Info(BreakInfo::pickaxe(0.8, ToolTier::WOOD, 4.0));
 		self::register("end_stone_bricks", new Opaque(new BID(Ids::END_STONE_BRICKS), "End Stone Bricks", $endBrickBreakInfo));
 		self::register("end_stone_brick_stairs", new Stair(new BID(Ids::END_STONE_BRICK_STAIRS), "End Stone Brick Stairs", $endBrickBreakInfo));
 
-		self::register("ender_chest", new EnderChest(new BID(Ids::ENDER_CHEST, TileEnderChest::class), "Ender Chest", new Info(BreakInfo::pickaxe(22.5, ToolTier::WOOD(), 3000.0))));
+		self::register("ender_chest", new EnderChest(new BID(Ids::ENDER_CHEST, TileEnderChest::class), "Ender Chest", new Info(BreakInfo::pickaxe(22.5, ToolTier::WOOD, 3000.0))));
 		self::register("farmland", new Farmland(new BID(Ids::FARMLAND), "Farmland", new Info(BreakInfo::shovel(0.6), [Tags::DIRT])));
 		self::register("fire", new Fire(new BID(Ids::FIRE), "Fire Block", new Info(BreakInfo::instant(), [Tags::FIRE])));
 
@@ -874,33 +887,35 @@ public function getBreakTime(Item $item) : float{
 		self::register("pink_tulip", new Flower(new BID(Ids::PINK_TULIP), "Pink Tulip", $flowerTypeInfo));
 		self::register("red_tulip", new Flower(new BID(Ids::RED_TULIP), "Red Tulip", $flowerTypeInfo));
 		self::register("white_tulip", new Flower(new BID(Ids::WHITE_TULIP), "White Tulip", $flowerTypeInfo));
+		self::register("torchflower", new Flower(new BID(Ids::TORCHFLOWER), "Torchflower", $flowerTypeInfo));
+		self::register("torchflower_crop", new TorchflowerCrop(new BID(Ids::TORCHFLOWER_CROP), "Torchflower Crop", new Info(BreakInfo::instant())));
 		self::register("flower_pot", new FlowerPot(new BID(Ids::FLOWER_POT, TileFlowerPot::class), "Flower Pot", new Info(BreakInfo::instant())));
 		self::register("frosted_ice", new FrostedIce(new BID(Ids::FROSTED_ICE), "Frosted Ice", new Info(BreakInfo::pickaxe(2.5))));
-		self::register("furnace", new Furnace(new BID(Ids::FURNACE, TileNormalFurnace::class), "Furnace", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD())), FurnaceType::FURNACE()));
-		self::register("blast_furnace", new Furnace(new BID(Ids::BLAST_FURNACE, TileBlastFurnace::class), "Blast Furnace", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD())), FurnaceType::BLAST_FURNACE()));
-		self::register("smoker", new Furnace(new BID(Ids::SMOKER, TileSmoker::class), "Smoker", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD())), FurnaceType::SMOKER()));
+		self::register("furnace", new Furnace(new BID(Ids::FURNACE, TileNormalFurnace::class), "Furnace", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)), FurnaceType::FURNACE));
+		self::register("blast_furnace", new Furnace(new BID(Ids::BLAST_FURNACE, TileBlastFurnace::class), "Blast Furnace", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)), FurnaceType::BLAST_FURNACE));
+		self::register("smoker", new Furnace(new BID(Ids::SMOKER, TileSmoker::class), "Smoker", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)), FurnaceType::SMOKER));
 
 		$glassBreakInfo = new Info(new BreakInfo(0.3));
 		self::register("glass", new Glass(new BID(Ids::GLASS), "Glass", $glassBreakInfo));
 		self::register("glass_pane", new GlassPane(new BID(Ids::GLASS_PANE), "Glass Pane", $glassBreakInfo));
-		self::register("glowing_obsidian", new GlowingObsidian(new BID(Ids::GLOWING_OBSIDIAN), "Glowing Obsidian", new Info(BreakInfo::pickaxe(10.0, ToolTier::DIAMOND(), 50.0))));
+		self::register("glowing_obsidian", new GlowingObsidian(new BID(Ids::GLOWING_OBSIDIAN), "Glowing Obsidian", new Info(BreakInfo::pickaxe(10.0, ToolTier::DIAMOND, 50.0))));
 		self::register("glowstone", new Glowstone(new BID(Ids::GLOWSTONE), "Glowstone", new Info(BreakInfo::pickaxe(0.3))));
 		self::register("glow_lichen", new GlowLichen(new BID(Ids::GLOW_LICHEN), "Glow Lichen", new Info(BreakInfo::axe(0.2, null, 0.2))));
-		self::register("gold", new Opaque(new BID(Ids::GOLD), "Gold Block", new Info(BreakInfo::pickaxe(3.0, ToolTier::IRON(), 30.0))));
+		self::register("gold", new Opaque(new BID(Ids::GOLD), "Gold Block", new Info(BreakInfo::pickaxe(3.0, ToolTier::IRON, 30.0))));
 
 		$grassBreakInfo = BreakInfo::shovel(0.6);
 		self::register("grass", new Grass(new BID(Ids::GRASS), "Grass", new Info($grassBreakInfo, [Tags::DIRT])));
 		self::register("grass_path", new GrassPath(new BID(Ids::GRASS_PATH), "Grass Path", new Info($grassBreakInfo)));
 		self::register("gravel", new Gravel(new BID(Ids::GRAVEL), "Gravel", new Info(BreakInfo::shovel(0.6))));
 
-		$hardenedClayBreakInfo = new Info(BreakInfo::pickaxe(1.25, ToolTier::WOOD(), 21.0));
+		$hardenedClayBreakInfo = new Info(BreakInfo::pickaxe(1.25, ToolTier::WOOD, 21.0));
 		self::register("hardened_clay", new HardenedClay(new BID(Ids::HARDENED_CLAY), "Hardened Clay", $hardenedClayBreakInfo));
 
 		$hardenedGlassBreakInfo = new Info(new BreakInfo(10.0));
 		self::register("hardened_glass", new HardenedGlass(new BID(Ids::HARDENED_GLASS), "Hardened Glass", $hardenedGlassBreakInfo));
 		self::register("hardened_glass_pane", new HardenedGlassPane(new BID(Ids::HARDENED_GLASS_PANE), "Hardened Glass Pane", $hardenedGlassBreakInfo));
 		self::register("hay_bale", new HayBale(new BID(Ids::HAY_BALE), "Hay Bale", new Info(new BreakInfo(0.5))));
-		self::register("hopper", new Hopper(new BID(Ids::HOPPER, TileHopper::class), "Hopper", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD(), 15.0))));
+		self::register("hopper", new Hopper(new BID(Ids::HOPPER, TileHopper::class), "Hopper", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD, 15.0))));
 		self::register("ice", new Ice(new BID(Ids::ICE), "Ice", new Info(BreakInfo::pickaxe(0.5))));
 
 		$updateBlockBreakInfo = new Info(new BreakInfo(1.0));
@@ -908,10 +923,10 @@ public function getBreakTime(Item $item) : float{
 		self::register("info_update2", new Opaque(new BID(Ids::INFO_UPDATE2), "ate!upd", $updateBlockBreakInfo));
 		self::register("invisible_bedrock", new Transparent(new BID(Ids::INVISIBLE_BEDROCK), "Invisible Bedrock", new Info(BreakInfo::indestructible())));
 
-		$ironBreakInfo = new Info(BreakInfo::pickaxe(5.0, ToolTier::STONE(), 30.0));
+		$ironBreakInfo = new Info(BreakInfo::pickaxe(5.0, ToolTier::STONE, 30.0));
 		self::register("iron", new Opaque(new BID(Ids::IRON), "Iron Block", $ironBreakInfo));
 		self::register("iron_bars", new Thin(new BID(Ids::IRON_BARS), "Iron Bars", $ironBreakInfo));
-		$ironDoorBreakInfo = new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD(), 25.0));
+		$ironDoorBreakInfo = new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 25.0));
 		self::register("iron_door", new Door(new BID(Ids::IRON_DOOR), "Iron Door", $ironDoorBreakInfo));
 		self::register("iron_trapdoor", new Trapdoor(new BID(Ids::IRON_TRAPDOOR), "Iron Trapdoor", $ironDoorBreakInfo));
 
@@ -922,21 +937,21 @@ public function getBreakTime(Item $item) : float{
 		self::register("jukebox", new Jukebox(new BID(Ids::JUKEBOX, TileJukebox::class), "Jukebox", new Info(BreakInfo::axe(0.8)))); //TODO: in PC the hardness is 2.0, not 0.8, unsure if this is a MCPE bug or not
 		self::register("ladder", new Ladder(new BID(Ids::LADDER), "Ladder", new Info(BreakInfo::axe(0.4))));
 
-		$lanternBreakInfo = new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD()));
+		$lanternBreakInfo = new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD));
 		self::register("lantern", new Lantern(new BID(Ids::LANTERN), "Lantern", $lanternBreakInfo, 15));
 		self::register("soul_lantern", new Lantern(new BID(Ids::SOUL_LANTERN), "Soul Lantern", $lanternBreakInfo, 10));
 
-		self::register("lapis_lazuli", new Opaque(new BID(Ids::LAPIS_LAZULI), "Lapis Lazuli Block", new Info(BreakInfo::pickaxe(3.0, ToolTier::STONE()))));
+		self::register("lapis_lazuli", new Opaque(new BID(Ids::LAPIS_LAZULI), "Lapis Lazuli Block", new Info(BreakInfo::pickaxe(3.0, ToolTier::STONE))));
 		self::register("lava", new Lava(new BID(Ids::LAVA), "Lava", new Info(BreakInfo::indestructible(500.0))));
 		self::register("lectern", new Lectern(new BID(Ids::LECTERN, TileLectern::class), "Lectern", new Info(BreakInfo::axe(2.0))));
 		self::register("lever", new Lever(new BID(Ids::LEVER), "Lever", new Info(new BreakInfo(0.5))));
-		self::register("magma", new Magma(new BID(Ids::MAGMA), "Magma Block", new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD()))));
+		self::register("magma", new Magma(new BID(Ids::MAGMA), "Magma Block", new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD))));
 		self::register("melon", new Melon(new BID(Ids::MELON), "Melon Block", new Info(BreakInfo::axe(1.0))));
 		self::register("melon_stem", new MelonStem(new BID(Ids::MELON_STEM), "Melon Stem", new Info(BreakInfo::instant())));
-		self::register("monster_spawner", new MonsterSpawner(new BID(Ids::MONSTER_SPAWNER, TileMonsterSpawner::class), "Monster Spawner", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD()))));
+		self::register("monster_spawner", new MonsterSpawner(new BID(Ids::MONSTER_SPAWNER, TileMonsterSpawner::class), "Monster Spawner", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD))));
 		self::register("mycelium", new Mycelium(new BID(Ids::MYCELIUM), "Mycelium", new Info(BreakInfo::shovel(0.6), [Tags::DIRT])));
 
-		$netherBrickBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD(), 30.0));
+		$netherBrickBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0));
 		self::register("nether_bricks", new Opaque(new BID(Ids::NETHER_BRICKS), "Nether Bricks", $netherBrickBreakInfo));
 		self::register("red_nether_bricks", new Opaque(new BID(Ids::RED_NETHER_BRICKS), "Red Nether Bricks", $netherBrickBreakInfo));
 		self::register("nether_brick_fence", new Fence(new BID(Ids::NETHER_BRICK_FENCE), "Nether Brick Fence", $netherBrickBreakInfo));
@@ -946,18 +961,18 @@ public function getBreakTime(Item $item) : float{
 		self::register("cracked_nether_bricks", new Opaque(new BID(Ids::CRACKED_NETHER_BRICKS), "Cracked Nether Bricks", $netherBrickBreakInfo));
 
 		self::register("nether_portal", new NetherPortal(new BID(Ids::NETHER_PORTAL), "Nether Portal", new Info(BreakInfo::indestructible(0.0))));
-		self::register("nether_reactor_core", new NetherReactor(new BID(Ids::NETHER_REACTOR_CORE), "Nether Reactor Core", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD()))));
+		self::register("nether_reactor_core", new NetherReactor(new BID(Ids::NETHER_REACTOR_CORE), "Nether Reactor Core", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD))));
 		self::register("nether_wart_block", new Opaque(new BID(Ids::NETHER_WART_BLOCK), "Nether Wart Block", new Info(new BreakInfo(1.0, ToolType::HOE))));
 		self::register("nether_wart", new NetherWartPlant(new BID(Ids::NETHER_WART), "Nether Wart", new Info(BreakInfo::instant())));
-		self::register("netherrack", new Netherrack(new BID(Ids::NETHERRACK), "Netherrack", new Info(BreakInfo::pickaxe(0.4, ToolTier::WOOD()))));
+		self::register("netherrack", new Netherrack(new BID(Ids::NETHERRACK), "Netherrack", new Info(BreakInfo::pickaxe(0.4, ToolTier::WOOD))));
 		self::register("note_block", new Note(new BID(Ids::NOTE_BLOCK, TileNote::class), "Note Block", new Info(BreakInfo::axe(0.8))));
-		self::register("obsidian", new Opaque(new BID(Ids::OBSIDIAN), "Obsidian", new Info(BreakInfo::pickaxe(35.0 /* 50 in PC */,  ToolTier::DIAMOND(), 6000.0))));
+		self::register("obsidian", new Opaque(new BID(Ids::OBSIDIAN), "Obsidian", new Info(BreakInfo::pickaxe(35.0 /* 50 in PC */,  ToolTier::DIAMOND, 6000.0))));
 		self::register("packed_ice", new PackedIce(new BID(Ids::PACKED_ICE), "Packed Ice", new Info(BreakInfo::pickaxe(0.5))));
 		self::register("podzol", new Podzol(new BID(Ids::PODZOL), "Podzol", new Info(BreakInfo::shovel(0.5), [Tags::DIRT])));
 		self::register("potatoes", new Potato(new BID(Ids::POTATOES), "Potato Block", new Info(BreakInfo::instant())));
 		self::register("powered_rail", new PoweredRail(new BID(Ids::POWERED_RAIL), "Powered Rail", $railBreakInfo));
 
-		$prismarineBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD(), 30.0));
+		$prismarineBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0));
 		self::register("prismarine", new Opaque(new BID(Ids::PRISMARINE), "Prismarine", $prismarineBreakInfo));
 		self::register("dark_prismarine", new Opaque(new BID(Ids::DARK_PRISMARINE), "Dark Prismarine", $prismarineBreakInfo));
 		self::register("prismarine_bricks", new Opaque(new BID(Ids::PRISMARINE_BRICKS), "Prismarine Bricks", $prismarineBreakInfo));
@@ -972,12 +987,12 @@ public function getBreakTime(Item $item) : float{
 
 		self::register("pumpkin_stem", new PumpkinStem(new BID(Ids::PUMPKIN_STEM), "Pumpkin Stem", new Info(BreakInfo::instant())));
 
-		$purpurBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD(), 30.0));
+		$purpurBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0));
 		self::register("purpur", new Opaque(new BID(Ids::PURPUR), "Purpur Block", $purpurBreakInfo));
 		self::register("purpur_pillar", new SimplePillar(new BID(Ids::PURPUR_PILLAR), "Purpur Pillar", $purpurBreakInfo));
 		self::register("purpur_stairs", new Stair(new BID(Ids::PURPUR_STAIRS), "Purpur Stairs", $purpurBreakInfo));
 
-		$quartzBreakInfo = new Info(BreakInfo::pickaxe(0.8, ToolTier::WOOD()));
+		$quartzBreakInfo = new Info(BreakInfo::pickaxe(0.8, ToolTier::WOOD));
 		self::register("quartz", new Opaque(new BID(Ids::QUARTZ), "Quartz Block", $quartzBreakInfo));
 		self::register("chiseled_quartz", new SimplePillar(new BID(Ids::CHISELED_QUARTZ), "Chiseled Quartz Block", $quartzBreakInfo));
 		self::register("quartz_pillar", new SimplePillar(new BID(Ids::QUARTZ_PILLAR), "Quartz Pillar", $quartzBreakInfo));
@@ -989,7 +1004,7 @@ public function getBreakTime(Item $item) : float{
 
 		self::register("rail", new Rail(new BID(Ids::RAIL), "Rail", $railBreakInfo));
 		self::register("red_mushroom", new RedMushroom(new BID(Ids::RED_MUSHROOM), "Red Mushroom", new Info(BreakInfo::instant(), [Tags::POTTABLE_PLANTS])));
-		self::register("redstone", new Redstone(new BID(Ids::REDSTONE), "Redstone Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD(), 30.0))));
+		self::register("redstone", new Redstone(new BID(Ids::REDSTONE), "Redstone Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 30.0))));
 		self::register("redstone_comparator", new RedstoneComparator(new BID(Ids::REDSTONE_COMPARATOR, TileComparator::class), "Redstone Comparator", new Info(BreakInfo::instant())));
 		self::register("redstone_lamp", new RedstoneLamp(new BID(Ids::REDSTONE_LAMP), "Redstone Lamp", new Info(new BreakInfo(0.3))));
 		self::register("redstone_repeater", new RedstoneRepeater(new BID(Ids::REDSTONE_REPEATER), "Redstone Repeater", new Info(BreakInfo::instant())));
@@ -1005,14 +1020,14 @@ public function getBreakTime(Item $item) : float{
 		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), 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()))));
+		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))));
 		self::register("soul_sand", new SoulSand(new BID(Ids::SOUL_SAND), "Soul Sand", new Info(BreakInfo::shovel(0.5))));
 		self::register("sponge", new Sponge(new BID(Ids::SPONGE), "Sponge", new Info(new BreakInfo(0.6, ToolType::HOE))));
 		$shulkerBoxBreakInfo = new Info(BreakInfo::pickaxe(2));
 		self::register("shulker_box", new ShulkerBox(new BID(Ids::SHULKER_BOX, TileShulkerBox::class), "Shulker Box", $shulkerBoxBreakInfo));
 
-		$stoneBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD(), 30.0));
+		$stoneBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0));
 		self::register(
 			"stone",
 			$stone = new class(new BID(Ids::STONE), "Stone", $stoneBreakInfo) extends Opaque{
@@ -1057,10 +1072,10 @@ public function isAffectedBySilkTouch() : bool{
 		self::register("mossy_stone_brick_stairs", new Stair(new BID(Ids::MOSSY_STONE_BRICK_STAIRS), "Mossy Stone Brick Stairs", $stoneBreakInfo));
 		self::register("stone_button", new StoneButton(new BID(Ids::STONE_BUTTON), "Stone Button", new Info(BreakInfo::pickaxe(0.5))));
 		self::register("stonecutter", new Stonecutter(new BID(Ids::STONECUTTER), "Stonecutter", new Info(BreakInfo::pickaxe(3.5))));
-		self::register("stone_pressure_plate", new StonePressurePlate(new BID(Ids::STONE_PRESSURE_PLATE), "Stone Pressure Plate", new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD()))));
+		self::register("stone_pressure_plate", new StonePressurePlate(new BID(Ids::STONE_PRESSURE_PLATE), "Stone Pressure Plate", new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD))));
 
 		//TODO: in the future this won't be the same for all the types
-		$stoneSlabBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD(), 30.0));
+		$stoneSlabBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0));
 
 		self::register("brick_slab", new Slab(new BID(Ids::BRICK_SLAB), "Brick", $stoneSlabBreakInfo));
 		self::register("cobblestone_slab", new Slab(new BID(Ids::COBBLESTONE_SLAB), "Cobblestone", $stoneSlabBreakInfo));
@@ -1092,7 +1107,7 @@ public function isAffectedBySilkTouch() : bool{
 		self::register("smooth_quartz_slab", new Slab(new BID(Ids::SMOOTH_QUARTZ_SLAB), "Smooth Quartz", $stoneSlabBreakInfo));
 		self::register("stone_slab", new Slab(new BID(Ids::STONE_SLAB), "Stone", $stoneSlabBreakInfo));
 
-		self::register("legacy_stonecutter", new Opaque(new BID(Ids::LEGACY_STONECUTTER), "Legacy Stonecutter", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD()))));
+		self::register("legacy_stonecutter", new Opaque(new BID(Ids::LEGACY_STONECUTTER), "Legacy Stonecutter", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD))));
 		self::register("sugarcane", new Sugarcane(new BID(Ids::SUGARCANE), "Sugarcane", new Info(BreakInfo::instant())));
 		self::register("sweet_berry_bush", new SweetBerryBush(new BID(Ids::SWEET_BERRY_BUSH), "Sweet Berry Bush", new Info(BreakInfo::instant())));
 		self::register("tnt", new TNT(new BID(Ids::TNT), "TNT", new Info(BreakInfo::instant())));
@@ -1113,7 +1128,7 @@ public function isAffectedBySilkTouch() : bool{
 		self::register("water", new Water(new BID(Ids::WATER), "Water", new Info(BreakInfo::indestructible(500.0))));
 		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()));
+		$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",
@@ -1140,16 +1155,16 @@ public function getBreakTime(Item $item) : float{
 		});
 		$saplingTypeInfo = new Info(BreakInfo::instant(), [Tags::POTTABLE_PLANTS]);
 
-		foreach(SaplingType::getAll() as $saplingType){
+		foreach(SaplingType::cases() as $saplingType){
 			$name = $saplingType->getDisplayName();
-			self::register($saplingType->name() . "_sapling", new Sapling(WoodLikeBlockIdHelper::getSaplingIdentifier($saplingType), $name . " Sapling", $saplingTypeInfo, $saplingType));
+			self::register(strtolower($saplingType->name) . "_sapling", new Sapling(WoodLikeBlockIdHelper::getSaplingIdentifier($saplingType), $name . " Sapling", $saplingTypeInfo, $saplingType));
 		}
-		foreach(LeavesType::getAll() as $leavesType){
+		foreach(LeavesType::cases() as $leavesType){
 			$name = $leavesType->getDisplayName();
-			self::register($leavesType->name() . "_leaves", new Leaves(WoodLikeBlockIdHelper::getLeavesIdentifier($leavesType), $name . " Leaves", $leavesBreakInfo, $leavesType));
+			self::register(strtolower($leavesType->name) . "_leaves", new Leaves(WoodLikeBlockIdHelper::getLeavesIdentifier($leavesType), $name . " Leaves", $leavesBreakInfo, $leavesType));
 		}
 
-		$sandstoneBreakInfo = new Info(BreakInfo::pickaxe(0.8, ToolTier::WOOD()));
+		$sandstoneBreakInfo = new Info(BreakInfo::pickaxe(0.8, ToolTier::WOOD));
 		self::register("red_sandstone_stairs", new Stair(new BID(Ids::RED_SANDSTONE_STAIRS), "Red Sandstone Stairs", $sandstoneBreakInfo));
 		self::register("smooth_red_sandstone_stairs", new Stair(new BID(Ids::SMOOTH_RED_SANDSTONE_STAIRS), "Smooth Red Sandstone Stairs", $sandstoneBreakInfo));
 		self::register("red_sandstone", new Opaque(new BID(Ids::RED_SANDSTONE), "Red Sandstone", $sandstoneBreakInfo));
@@ -1164,7 +1179,7 @@ public function getBreakTime(Item $item) : float{
 		self::register("cut_sandstone", new Opaque(new BID(Ids::CUT_SANDSTONE), "Cut Sandstone", $sandstoneBreakInfo));
 		self::register("smooth_sandstone", new Opaque(new BID(Ids::SMOOTH_SANDSTONE), "Smooth Sandstone", $sandstoneBreakInfo));
 
-		self::register("glazed_terracotta", new GlazedTerracotta(new BID(Ids::GLAZED_TERRACOTTA), "Glazed Terracotta", new Info(BreakInfo::pickaxe(1.4, ToolTier::WOOD()))));
+		self::register("glazed_terracotta", new GlazedTerracotta(new BID(Ids::GLAZED_TERRACOTTA), "Glazed Terracotta", new Info(BreakInfo::pickaxe(1.4, ToolTier::WOOD))));
 		self::register("dyed_shulker_box", new DyedShulkerBox(new BID(Ids::DYED_SHULKER_BOX, TileShulkerBox::class), "Dyed Shulker Box", $shulkerBoxBreakInfo));
 		self::register("stained_glass", new StainedGlass(new BID(Ids::STAINED_GLASS), "Stained Glass", $glassBreakInfo));
 		self::register("stained_glass_pane", new StainedGlassPane(new BID(Ids::STAINED_GLASS_PANE), "Stained Glass Pane", $glassBreakInfo));
@@ -1172,7 +1187,7 @@ public function getBreakTime(Item $item) : float{
 		self::register("stained_hardened_glass", new StainedHardenedGlass(new BID(Ids::STAINED_HARDENED_GLASS), "Stained Hardened Glass", $hardenedGlassBreakInfo));
 		self::register("stained_hardened_glass_pane", new StainedHardenedGlassPane(new BID(Ids::STAINED_HARDENED_GLASS_PANE), "Stained Hardened Glass Pane", $hardenedGlassBreakInfo));
 		self::register("carpet", new Carpet(new BID(Ids::CARPET), "Carpet", new Info(new BreakInfo(0.1))));
-		self::register("concrete", new Concrete(new BID(Ids::CONCRETE), "Concrete", new Info(BreakInfo::pickaxe(1.8, ToolTier::WOOD()))));
+		self::register("concrete", new Concrete(new BID(Ids::CONCRETE), "Concrete", new Info(BreakInfo::pickaxe(1.8, ToolTier::WOOD))));
 		self::register("concrete_powder", new ConcretePowder(new BID(Ids::CONCRETE_POWDER), "Concrete Powder", new Info(BreakInfo::shovel(0.5))));
 		self::register("wool", new Wool(new BID(Ids::WOOL), "Wool", new Info(new class(0.8, ToolType::SHEARS) extends BreakInfo{
 			public function getBreakTime(Item $item) : float{
@@ -1186,7 +1201,7 @@ public function getBreakTime(Item $item) : float{
 		})));
 
 		//TODO: in the future these won't all have the same hardness; they only do now because of the old metadata crap
-		$wallBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD(), 30.0));
+		$wallBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0));
 		self::register("cobblestone_wall", new Wall(new BID(Ids::COBBLESTONE_WALL), "Cobblestone Wall", $wallBreakInfo));
 		self::register("andesite_wall", new Wall(new BID(Ids::ANDESITE_WALL), "Andesite Wall", $wallBreakInfo));
 		self::register("brick_wall", new Wall(new BID(Ids::BRICK_WALL), "Brick Wall", $wallBreakInfo));
@@ -1204,7 +1219,7 @@ public function getBreakTime(Item $item) : float{
 
 		self::registerElements();
 
-		$chemistryTableBreakInfo = new Info(BreakInfo::pickaxe(2.5, ToolTier::WOOD()));
+		$chemistryTableBreakInfo = new Info(BreakInfo::pickaxe(2.5, ToolTier::WOOD));
 		self::register("compound_creator", new ChemistryTable(new BID(Ids::COMPOUND_CREATOR), "Compound Creator", $chemistryTableBreakInfo));
 		self::register("element_constructor", new ChemistryTable(new BID(Ids::ELEMENT_CONSTRUCTOR), "Element Constructor", $chemistryTableBreakInfo));
 		self::register("lab_table", new ChemistryTable(new BID(Ids::LAB_TABLE), "Lab Table", $chemistryTableBreakInfo));
@@ -1262,9 +1277,9 @@ private static function registerWoodenBlocks() : void{
 		$woodenButtonBreakInfo = new Info(BreakInfo::axe(0.5));
 		$woodenPressurePlateBreakInfo = new Info(BreakInfo::axe(0.5));
 
-		foreach(WoodType::getAll() as $woodType){
+		foreach(WoodType::cases() as $woodType){
 			$name = $woodType->getDisplayName();
-			$idName = fn(string $suffix) => $woodType->name() . "_" . $suffix;
+			$idName = fn(string $suffix) => strtolower($woodType->name) . "_" . $suffix;
 
 			self::register($idName(mb_strtolower($woodType->getStandardLogSuffix() ?? "log", 'US-ASCII')), new Wood(WoodLikeBlockIdHelper::getLogIdentifier($woodType), $name . " " . ($woodType->getStandardLogSuffix() ?? "Log"), $logBreakInfo, $woodType));
 			self::register($idName(mb_strtolower($woodType->getAllSidedLogSuffix() ?? "wood", 'US-ASCII')), new Wood(WoodLikeBlockIdHelper::getAllSidedLogIdentifier($woodType), $name . " " . ($woodType->getAllSidedLogSuffix() ?? "Wood"), $logBreakInfo, $woodType));
@@ -1427,26 +1442,26 @@ private static function registerElements() : void{
 
 	private static function registerOres() : void{
 		$stoneOreBreakInfo = fn(ToolTier $toolTier) => new Info(BreakInfo::pickaxe(3.0, $toolTier));
-		self::register("coal_ore", new CoalOre(new BID(Ids::COAL_ORE), "Coal Ore", $stoneOreBreakInfo(ToolTier::WOOD())));
-		self::register("copper_ore", new CopperOre(new BID(Ids::COPPER_ORE), "Copper Ore", $stoneOreBreakInfo(ToolTier::STONE())));
-		self::register("diamond_ore", new DiamondOre(new BID(Ids::DIAMOND_ORE), "Diamond Ore", $stoneOreBreakInfo(ToolTier::IRON())));
-		self::register("emerald_ore", new EmeraldOre(new BID(Ids::EMERALD_ORE), "Emerald Ore", $stoneOreBreakInfo(ToolTier::IRON())));
-		self::register("gold_ore", new GoldOre(new BID(Ids::GOLD_ORE), "Gold Ore", $stoneOreBreakInfo(ToolTier::IRON())));
-		self::register("iron_ore", new IronOre(new BID(Ids::IRON_ORE), "Iron Ore", $stoneOreBreakInfo(ToolTier::STONE())));
-		self::register("lapis_lazuli_ore", new LapisOre(new BID(Ids::LAPIS_LAZULI_ORE), "Lapis Lazuli Ore", $stoneOreBreakInfo(ToolTier::STONE())));
-		self::register("redstone_ore", new RedstoneOre(new BID(Ids::REDSTONE_ORE), "Redstone Ore", $stoneOreBreakInfo(ToolTier::IRON())));
+		self::register("coal_ore", new CoalOre(new BID(Ids::COAL_ORE), "Coal Ore", $stoneOreBreakInfo(ToolTier::WOOD)));
+		self::register("copper_ore", new CopperOre(new BID(Ids::COPPER_ORE), "Copper Ore", $stoneOreBreakInfo(ToolTier::STONE)));
+		self::register("diamond_ore", new DiamondOre(new BID(Ids::DIAMOND_ORE), "Diamond Ore", $stoneOreBreakInfo(ToolTier::IRON)));
+		self::register("emerald_ore", new EmeraldOre(new BID(Ids::EMERALD_ORE), "Emerald Ore", $stoneOreBreakInfo(ToolTier::IRON)));
+		self::register("gold_ore", new GoldOre(new BID(Ids::GOLD_ORE), "Gold Ore", $stoneOreBreakInfo(ToolTier::IRON)));
+		self::register("iron_ore", new IronOre(new BID(Ids::IRON_ORE), "Iron Ore", $stoneOreBreakInfo(ToolTier::STONE)));
+		self::register("lapis_lazuli_ore", new LapisOre(new BID(Ids::LAPIS_LAZULI_ORE), "Lapis Lazuli Ore", $stoneOreBreakInfo(ToolTier::STONE)));
+		self::register("redstone_ore", new RedstoneOre(new BID(Ids::REDSTONE_ORE), "Redstone Ore", $stoneOreBreakInfo(ToolTier::IRON)));
 
 		$deepslateOreBreakInfo = fn(ToolTier $toolTier) => new Info(BreakInfo::pickaxe(4.5, $toolTier));
-		self::register("deepslate_coal_ore", new CoalOre(new BID(Ids::DEEPSLATE_COAL_ORE), "Deepslate Coal Ore", $deepslateOreBreakInfo(ToolTier::WOOD())));
-		self::register("deepslate_copper_ore", new CopperOre(new BID(Ids::DEEPSLATE_COPPER_ORE), "Deepslate Copper Ore", $deepslateOreBreakInfo(ToolTier::STONE())));
-		self::register("deepslate_diamond_ore", new DiamondOre(new BID(Ids::DEEPSLATE_DIAMOND_ORE), "Deepslate Diamond Ore", $deepslateOreBreakInfo(ToolTier::IRON())));
-		self::register("deepslate_emerald_ore", new EmeraldOre(new BID(Ids::DEEPSLATE_EMERALD_ORE), "Deepslate Emerald Ore", $deepslateOreBreakInfo(ToolTier::IRON())));
-		self::register("deepslate_gold_ore", new GoldOre(new BID(Ids::DEEPSLATE_GOLD_ORE), "Deepslate Gold Ore", $deepslateOreBreakInfo(ToolTier::IRON())));
-		self::register("deepslate_iron_ore", new IronOre(new BID(Ids::DEEPSLATE_IRON_ORE), "Deepslate Iron Ore", $deepslateOreBreakInfo(ToolTier::STONE())));
-		self::register("deepslate_lapis_lazuli_ore", new LapisOre(new BID(Ids::DEEPSLATE_LAPIS_LAZULI_ORE), "Deepslate Lapis Lazuli Ore", $deepslateOreBreakInfo(ToolTier::STONE())));
-		self::register("deepslate_redstone_ore", new RedstoneOre(new BID(Ids::DEEPSLATE_REDSTONE_ORE), "Deepslate Redstone Ore", $deepslateOreBreakInfo(ToolTier::IRON())));
-
-		$netherrackOreBreakInfo = new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD()));
+		self::register("deepslate_coal_ore", new CoalOre(new BID(Ids::DEEPSLATE_COAL_ORE), "Deepslate Coal Ore", $deepslateOreBreakInfo(ToolTier::WOOD)));
+		self::register("deepslate_copper_ore", new CopperOre(new BID(Ids::DEEPSLATE_COPPER_ORE), "Deepslate Copper Ore", $deepslateOreBreakInfo(ToolTier::STONE)));
+		self::register("deepslate_diamond_ore", new DiamondOre(new BID(Ids::DEEPSLATE_DIAMOND_ORE), "Deepslate Diamond Ore", $deepslateOreBreakInfo(ToolTier::IRON)));
+		self::register("deepslate_emerald_ore", new EmeraldOre(new BID(Ids::DEEPSLATE_EMERALD_ORE), "Deepslate Emerald Ore", $deepslateOreBreakInfo(ToolTier::IRON)));
+		self::register("deepslate_gold_ore", new GoldOre(new BID(Ids::DEEPSLATE_GOLD_ORE), "Deepslate Gold Ore", $deepslateOreBreakInfo(ToolTier::IRON)));
+		self::register("deepslate_iron_ore", new IronOre(new BID(Ids::DEEPSLATE_IRON_ORE), "Deepslate Iron Ore", $deepslateOreBreakInfo(ToolTier::STONE)));
+		self::register("deepslate_lapis_lazuli_ore", new LapisOre(new BID(Ids::DEEPSLATE_LAPIS_LAZULI_ORE), "Deepslate Lapis Lazuli Ore", $deepslateOreBreakInfo(ToolTier::STONE)));
+		self::register("deepslate_redstone_ore", new RedstoneOre(new BID(Ids::DEEPSLATE_REDSTONE_ORE), "Deepslate Redstone Ore", $deepslateOreBreakInfo(ToolTier::IRON)));
+
+		$netherrackOreBreakInfo = new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD));
 		self::register("nether_quartz_ore", new NetherQuartzOre(new BID(Ids::NETHER_QUARTZ_ORE), "Nether Quartz Ore", $netherrackOreBreakInfo));
 		self::register("nether_gold_ore", new NetherGoldOre(new BID(Ids::NETHER_GOLD_ORE), "Nether Gold Ore", $netherrackOreBreakInfo));
 	}
@@ -1478,20 +1493,20 @@ private static function registerBlocksR14() : void{
 
 	private static function registerBlocksR16() : void{
 		//for some reason, slabs have weird hardness like the legacy ones
-		$slabBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD(), 30.0));
+		$slabBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0));
 
-		self::register("ancient_debris", new Opaque(new BID(Ids::ANCIENT_DEBRIS), "Ancient Debris", new Info(BreakInfo::pickaxe(30, ToolTier::DIAMOND(), 3600.0))));
-		$netheriteBreakInfo = new Info(BreakInfo::pickaxe(50, ToolTier::DIAMOND(), 3600.0));
+		self::register("ancient_debris", new Opaque(new BID(Ids::ANCIENT_DEBRIS), "Ancient Debris", new Info(BreakInfo::pickaxe(30, ToolTier::DIAMOND, 3600.0))));
+		$netheriteBreakInfo = new Info(BreakInfo::pickaxe(50, ToolTier::DIAMOND, 3600.0));
 		self::register("netherite", new class(new BID(Ids::NETHERITE), "Netherite Block", $netheriteBreakInfo) extends Opaque{
 			public function isFireProofAsItem() : bool{ return true; }
 		});
 
-		$basaltBreakInfo = new Info(BreakInfo::pickaxe(1.25, ToolTier::WOOD(), 21.0));
+		$basaltBreakInfo = new Info(BreakInfo::pickaxe(1.25, ToolTier::WOOD, 21.0));
 		self::register("basalt", new SimplePillar(new BID(Ids::BASALT), "Basalt", $basaltBreakInfo));
 		self::register("polished_basalt", new SimplePillar(new BID(Ids::POLISHED_BASALT), "Polished Basalt", $basaltBreakInfo));
 		self::register("smooth_basalt", new Opaque(new BID(Ids::SMOOTH_BASALT), "Smooth Basalt", $basaltBreakInfo));
 
-		$blackstoneBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD(), 30.0));
+		$blackstoneBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0));
 		self::register("blackstone", new Opaque(new BID(Ids::BLACKSTONE), "Blackstone", $blackstoneBreakInfo));
 		self::register("blackstone_slab", new Slab(new BID(Ids::BLACKSTONE_SLAB), "Blackstone", $slabBreakInfo));
 		self::register("blackstone_stairs", new Stair(new BID(Ids::BLACKSTONE_STAIRS), "Blackstone Stairs", $blackstoneBreakInfo));
@@ -1503,7 +1518,7 @@ public function isFireProofAsItem() : bool{ return true; }
 		$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())), 20));
+		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));
@@ -1527,33 +1542,37 @@ public function getLightLevel() : int{ return 15; }
 		});
 
 		self::register("warped_wart_block", new Opaque(new BID(Ids::WARPED_WART_BLOCK), "Warped Wart Block", new Info(new BreakInfo(1.0, ToolType::HOE))));
-		self::register("crying_obsidian", new class(new BID(Ids::CRYING_OBSIDIAN), "Crying Obsidian", new Info(BreakInfo::pickaxe(35.0 /* 50 in Java */, ToolTier::DIAMOND(), 6000.0))) extends Opaque{
+		self::register("crying_obsidian", new class(new BID(Ids::CRYING_OBSIDIAN), "Crying Obsidian", new Info(BreakInfo::pickaxe(35.0 /* 50 in Java */, ToolTier::DIAMOND, 6000.0))) extends Opaque{
 			public function getLightLevel() : int{ return 10;}
 		});
 
 		self::register("twisting_vines", new NetherVines(new BID(Ids::TWISTING_VINES), "Twisting Vines", new Info(BreakInfo::instant()), Facing::UP));
 		self::register("weeping_vines", new NetherVines(new BID(Ids::WEEPING_VINES), "Weeping Vines", new Info(BreakInfo::instant()), Facing::DOWN));
 
-		self::register("chain", new Chain(new BID(Ids::CHAIN), "Chain", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD()))));
+		$netherRootsInfo = new Info(BreakInfo::instant(), [Tags::POTTABLE_PLANTS]);
+		self::register("crimson_roots", new NetherRoots(new BID(Ids::CRIMSON_ROOTS), "Crimson Roots", $netherRootsInfo));
+		self::register("warped_roots", new NetherRoots(new BID(Ids::WARPED_ROOTS), "Warped Roots", $netherRootsInfo));
+
+		self::register("chain", new Chain(new BID(Ids::CHAIN), "Chain", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD))));
 	}
 
 	private static function registerBlocksR17() : void{
 		//in java this can be acquired using any tool - seems to be a parity issue in bedrock
-		self::register("amethyst", new class(new BID(Ids::AMETHYST), "Amethyst", new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD()))) extends Opaque{
-			public function onProjectileHit(Projectile $projectile, RayTraceResult $hitResult) : void{
-				$this->position->getWorld()->addSound($this->position, new AmethystBlockChimeSound());
-				$this->position->getWorld()->addSound($this->position, new BlockPunchSound($this));
-			}
+		$amethystInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD));
+		self::register("amethyst", new class(new BID(Ids::AMETHYST), "Amethyst", $amethystInfo) extends Opaque{
+			use AmethystTrait;
 		});
+		self::register("budding_amethyst", new BuddingAmethyst(new BID(Ids::BUDDING_AMETHYST), "Budding Amethyst", $amethystInfo));
+		self::register("amethyst_cluster", new AmethystCluster(new BID(Ids::AMETHYST_CLUSTER), "Amethyst Cluster", $amethystInfo));
 
-		self::register("calcite", new Opaque(new BID(Ids::CALCITE), "Calcite", new Info(BreakInfo::pickaxe(0.75, ToolTier::WOOD()))));
-		self::register("tuff", new Opaque(new BID(Ids::TUFF), "Tuff", new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD(), 30.0))));
+		self::register("calcite", new Opaque(new BID(Ids::CALCITE), "Calcite", new Info(BreakInfo::pickaxe(0.75, ToolTier::WOOD))));
+		self::register("tuff", new Opaque(new BID(Ids::TUFF), "Tuff", new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0))));
 
-		self::register("raw_copper", new Opaque(new BID(Ids::RAW_COPPER), "Raw Copper Block", new Info(BreakInfo::pickaxe(5, ToolTier::STONE(), 30.0))));
-		self::register("raw_gold", new Opaque(new BID(Ids::RAW_GOLD), "Raw Gold Block", new Info(BreakInfo::pickaxe(5, ToolTier::IRON(), 30.0))));
-		self::register("raw_iron", new Opaque(new BID(Ids::RAW_IRON), "Raw Iron Block", new Info(BreakInfo::pickaxe(5, ToolTier::STONE(), 30.0))));
+		self::register("raw_copper", new Opaque(new BID(Ids::RAW_COPPER), "Raw Copper Block", new Info(BreakInfo::pickaxe(5, ToolTier::STONE, 30.0))));
+		self::register("raw_gold", new Opaque(new BID(Ids::RAW_GOLD), "Raw Gold Block", new Info(BreakInfo::pickaxe(5, ToolTier::IRON, 30.0))));
+		self::register("raw_iron", new Opaque(new BID(Ids::RAW_IRON), "Raw Iron Block", new Info(BreakInfo::pickaxe(5, ToolTier::STONE, 30.0))));
 
-		$deepslateBreakInfo = new Info(BreakInfo::pickaxe(3, ToolTier::WOOD(), 18.0));
+		$deepslateBreakInfo = new Info(BreakInfo::pickaxe(3, ToolTier::WOOD, 18.0));
 		self::register("deepslate", new class(new BID(Ids::DEEPSLATE), "Deepslate", $deepslateBreakInfo) extends SimplePillar{
 			public function getDropsForCompatibleTool(Item $item) : array{
 				return [VanillaBlocks::COBBLED_DEEPSLATE()->asItem()];
@@ -1565,29 +1584,29 @@ public function isAffectedBySilkTouch() : bool{
 		});
 
 		//TODO: parity issue here - in Java this has a hardness of 3.0, but in bedrock it's 3.5
-		self::register("chiseled_deepslate", new Opaque(new BID(Ids::CHISELED_DEEPSLATE), "Chiseled Deepslate", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD(), 18.0))));
+		self::register("chiseled_deepslate", new Opaque(new BID(Ids::CHISELED_DEEPSLATE), "Chiseled Deepslate", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0))));
 
-		$deepslateBrickBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD(), 18.0));
+		$deepslateBrickBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0));
 		self::register("deepslate_bricks", new Opaque(new BID(Ids::DEEPSLATE_BRICKS), "Deepslate Bricks", $deepslateBrickBreakInfo));
 		self::register("deepslate_brick_slab", new Slab(new BID(Ids::DEEPSLATE_BRICK_SLAB), "Deepslate Brick", $deepslateBrickBreakInfo));
 		self::register("deepslate_brick_stairs", new Stair(new BID(Ids::DEEPSLATE_BRICK_STAIRS), "Deepslate Brick Stairs", $deepslateBrickBreakInfo));
 		self::register("deepslate_brick_wall", new Wall(new BID(Ids::DEEPSLATE_BRICK_WALL), "Deepslate Brick Wall", $deepslateBrickBreakInfo));
 		self::register("cracked_deepslate_bricks", new Opaque(new BID(Ids::CRACKED_DEEPSLATE_BRICKS), "Cracked Deepslate Bricks", $deepslateBrickBreakInfo));
 
-		$deepslateTilesBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD(), 18.0));
+		$deepslateTilesBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0));
 		self::register("deepslate_tiles", new Opaque(new BID(Ids::DEEPSLATE_TILES), "Deepslate Tiles", $deepslateTilesBreakInfo));
 		self::register("deepslate_tile_slab", new Slab(new BID(Ids::DEEPSLATE_TILE_SLAB), "Deepslate Tile", $deepslateTilesBreakInfo));
 		self::register("deepslate_tile_stairs", new Stair(new BID(Ids::DEEPSLATE_TILE_STAIRS), "Deepslate Tile Stairs", $deepslateTilesBreakInfo));
 		self::register("deepslate_tile_wall", new Wall(new BID(Ids::DEEPSLATE_TILE_WALL), "Deepslate Tile Wall", $deepslateTilesBreakInfo));
 		self::register("cracked_deepslate_tiles", new Opaque(new BID(Ids::CRACKED_DEEPSLATE_TILES), "Cracked Deepslate Tiles", $deepslateTilesBreakInfo));
 
-		$cobbledDeepslateBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD(), 18.0));
+		$cobbledDeepslateBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0));
 		self::register("cobbled_deepslate", new Opaque(new BID(Ids::COBBLED_DEEPSLATE), "Cobbled Deepslate", $cobbledDeepslateBreakInfo));
 		self::register("cobbled_deepslate_slab", new Slab(new BID(Ids::COBBLED_DEEPSLATE_SLAB), "Cobbled Deepslate", $cobbledDeepslateBreakInfo));
 		self::register("cobbled_deepslate_stairs", new Stair(new BID(Ids::COBBLED_DEEPSLATE_STAIRS), "Cobbled Deepslate Stairs", $cobbledDeepslateBreakInfo));
 		self::register("cobbled_deepslate_wall", new Wall(new BID(Ids::COBBLED_DEEPSLATE_WALL), "Cobbled Deepslate Wall", $cobbledDeepslateBreakInfo));
 
-		$polishedDeepslateBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD(), 18.0));
+		$polishedDeepslateBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0));
 		self::register("polished_deepslate", new Opaque(new BID(Ids::POLISHED_DEEPSLATE), "Polished Deepslate", $polishedDeepslateBreakInfo));
 		self::register("polished_deepslate_slab", new Slab(new BID(Ids::POLISHED_DEEPSLATE_SLAB), "Polished Deepslate", $polishedDeepslateBreakInfo));
 		self::register("polished_deepslate_stairs", new Stair(new BID(Ids::POLISHED_DEEPSLATE_STAIRS), "Polished Deepslate Stairs", $polishedDeepslateBreakInfo));
@@ -1596,7 +1615,7 @@ public function isAffectedBySilkTouch() : bool{
 		self::register("tinted_glass", new TintedGlass(new BID(Ids::TINTED_GLASS), "Tinted Glass", new Info(new BreakInfo(0.3))));
 
 		//blast resistance should be 30 if we were matched with java :(
-		$copperBreakInfo = new Info(BreakInfo::pickaxe(3.0, ToolTier::STONE(), 18.0));
+		$copperBreakInfo = new Info(BreakInfo::pickaxe(3.0, ToolTier::STONE, 18.0));
 		self::register("lightning_rod", new LightningRod(new BID(Ids::LIGHTNING_ROD), "Lightning Rod", $copperBreakInfo));
 
 		self::register("copper", new Copper(new BID(Ids::COPPER), "Copper Block", $copperBreakInfo));
@@ -1630,7 +1649,7 @@ private static function registerMudBlocks() : void{
 		self::register("mud", new Opaque(new BID(Ids::MUD), "Mud", new Info(BreakInfo::shovel(0.5), [Tags::MUD])));
 		self::register("packed_mud", new Opaque(new BID(Ids::PACKED_MUD), "Packed Mud", new Info(BreakInfo::pickaxe(1.0, null, 15.0))));
 
-		$mudBricksBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD(), 30.0));
+		$mudBricksBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0));
 
 		self::register("mud_bricks", new Opaque(new BID(Ids::MUD_BRICKS), "Mud Bricks", $mudBricksBreakInfo));
 		self::register("mud_brick_slab", new Slab(new BID(Ids::MUD_BRICK_SLAB), "Mud Brick", $mudBricksBreakInfo));
@@ -1639,7 +1658,7 @@ private static function registerMudBlocks() : void{
 	}
 
 	private static function registerCauldronBlocks() : void{
-		$cauldronBreakInfo = new Info(BreakInfo::pickaxe(2, ToolTier::WOOD()));
+		$cauldronBreakInfo = new Info(BreakInfo::pickaxe(2, ToolTier::WOOD));
 
 		self::register("cauldron", new Cauldron(new BID(Ids::CAULDRON, TileCauldron::class), "Cauldron", $cauldronBreakInfo));
 		self::register("water_cauldron", new WaterCauldron(new BID(Ids::WATER_CAULDRON, TileCauldron::class), "Water Cauldron", $cauldronBreakInfo));
diff --git a/src/block/Wall.php b/src/block/Wall.php
index 87ee0b1bdbd..520ced8eb00 100644
--- a/src/block/Wall.php
+++ b/src/block/Wall.php
@@ -101,9 +101,9 @@ protected function recalculateConnections() : bool{
 
 		foreach(Facing::HORIZONTAL as $facing){
 			$block = $this->getSide($facing);
-			if($block instanceof static || $block instanceof FenceGate || $block instanceof Thin || $block->getSupportType(Facing::opposite($facing))->equals(SupportType::FULL())){
+			if($block instanceof static || $block instanceof FenceGate || $block instanceof Thin || $block->getSupportType(Facing::opposite($facing)) === SupportType::FULL){
 				if(!isset($this->connections[$facing])){
-					$this->connections[$facing] = WallConnectionType::SHORT();
+					$this->connections[$facing] = WallConnectionType::SHORT;
 					$changed++;
 				}
 			}elseif(isset($this->connections[$facing])){
@@ -152,6 +152,6 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function getSupportType(int $facing) : SupportType{
-		return Facing::axis($facing) === Axis::Y ? SupportType::CENTER() : SupportType::NONE();
+		return Facing::axis($facing) === Axis::Y ? SupportType::CENTER : SupportType::NONE;
 	}
 }
diff --git a/src/block/WaterCauldron.php b/src/block/WaterCauldron.php
index 6a3c95048fa..e470aa6cb85 100644
--- a/src/block/WaterCauldron.php
+++ b/src/block/WaterCauldron.php
@@ -110,10 +110,10 @@ public function getEmptySound() : Sound{
 	public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
 		$world = $this->position->getWorld();
 		if(($dyeColor = match($item->getTypeId()){
-				ItemTypeIds::LAPIS_LAZULI => DyeColor::BLUE(),
-				ItemTypeIds::INK_SAC => DyeColor::BLACK(),
-				ItemTypeIds::COCOA_BEANS => DyeColor::BROWN(),
-				ItemTypeIds::BONE_MEAL => DyeColor::WHITE(),
+				ItemTypeIds::LAPIS_LAZULI => DyeColor::BLUE,
+				ItemTypeIds::INK_SAC => DyeColor::BLACK,
+				ItemTypeIds::COCOA_BEANS => DyeColor::BROWN,
+				ItemTypeIds::BONE_MEAL => DyeColor::WHITE,
 				ItemTypeIds::DYE => $item instanceof Dye ? $item->getColor() : null,
 				default => null
 			}) !== null && ($newColor = $dyeColor->getRgbValue())->toRGBA() !== $this->customWaterColor?->toRGBA()
@@ -123,7 +123,7 @@ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player
 
 			$item->pop();
 		}elseif($item instanceof Potion || $item instanceof SplashPotion){ //TODO: lingering potion
-			if($item->getType()->equals(PotionType::WATER())){
+			if($item->getType() === PotionType::WATER){
 				$this->setCustomWaterColor(null)->addFillLevels(self::WATER_BOTTLE_FILL_AMOUNT, $item, VanillaItems::GLASS_BOTTLE(), $returnedItems);
 			}else{
 				$this->mix($item, VanillaItems::GLASS_BOTTLE(), $returnedItems);
@@ -170,7 +170,7 @@ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player
 			match($item->getTypeId()){
 				ItemTypeIds::WATER_BUCKET => $this->setCustomWaterColor(null)->addFillLevels(self::MAX_FILL_LEVEL, $item, VanillaItems::BUCKET(), $returnedItems),
 				ItemTypeIds::BUCKET => $this->removeFillLevels(self::MAX_FILL_LEVEL, $item, VanillaItems::WATER_BUCKET(), $returnedItems),
-				ItemTypeIds::GLASS_BOTTLE => $this->removeFillLevels(self::WATER_BOTTLE_FILL_AMOUNT, $item, VanillaItems::POTION()->setType(PotionType::WATER()), $returnedItems),
+				ItemTypeIds::GLASS_BOTTLE => $this->removeFillLevels(self::WATER_BOTTLE_FILL_AMOUNT, $item, VanillaItems::POTION()->setType(PotionType::WATER), $returnedItems),
 				ItemTypeIds::LAVA_BUCKET, ItemTypeIds::POWDER_SNOW_BUCKET => $this->mix($item, VanillaItems::BUCKET(), $returnedItems),
 				default => null
 			};
diff --git a/src/block/WaterLily.php b/src/block/WaterLily.php
index 8263330f64b..5dfb0d74a1f 100644
--- a/src/block/WaterLily.php
+++ b/src/block/WaterLily.php
@@ -23,14 +23,15 @@
 
 namespace pocketmine\block;
 
-use pocketmine\item\Item;
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\math\AxisAlignedBB;
 use pocketmine\math\Facing;
 use pocketmine\math\Vector3;
-use pocketmine\player\Player;
-use pocketmine\world\BlockTransaction;
 
 class WaterLily extends Flowable{
+	use StaticSupportTrait {
+		canBePlacedAt as supportedWhenPlacedAt;
+	}
 
 	/**
 	 * @return AxisAlignedBB[]
@@ -40,23 +41,10 @@ protected function recalculateCollisionBoxes() : array{
 	}
 
 	public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
-		return !$blockReplace instanceof Water && parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
-	}
-
-	private function canBeSupportedBy(Block $block) : bool{
-		return $block instanceof Water;
-	}
-
-	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		if(!$this->canBeSupportedBy($blockReplace->getSide(Facing::DOWN))){
-			return false;
-		}
-		return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
+		return !$blockReplace instanceof Water && $this->supportedWhenPlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
 	}
 
-	public function onNearbyBlockChange() : void{
-		if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
-			$this->position->getWorld()->useBreakOn($this->position);
-		}
+	private function canBeSupportedAt(Block $block) : bool{
+		return $block->getSide(Facing::DOWN) instanceof Water;
 	}
 }
diff --git a/src/block/WitherRose.php b/src/block/WitherRose.php
index 696b2611507..8c0977b3753 100644
--- a/src/block/WitherRose.php
+++ b/src/block/WitherRose.php
@@ -23,23 +23,22 @@
 
 namespace pocketmine\block;
 
+use pocketmine\block\utils\StaticSupportTrait;
 use pocketmine\entity\effect\EffectInstance;
 use pocketmine\entity\effect\VanillaEffects;
 use pocketmine\entity\Entity;
 use pocketmine\entity\Living;
-use pocketmine\item\Item;
 use pocketmine\math\Facing;
-use pocketmine\math\Vector3;
-use pocketmine\player\Player;
-use pocketmine\world\BlockTransaction;
 
 class WitherRose extends Flowable{
+	use StaticSupportTrait;
 
-	private function canBeSupportedBy(Block $block) : bool{
+	private function canBeSupportedAt(Block $block) : bool{
+		$supportBlock = $block->getSide(Facing::DOWN);
 		return
-			$block->hasTypeTag(BlockTypeTags::DIRT) ||
-			$block->hasTypeTag(BlockTypeTags::MUD) ||
-			match($block->getTypeId()){
+			$supportBlock->hasTypeTag(BlockTypeTags::DIRT) ||
+			$supportBlock->hasTypeTag(BlockTypeTags::MUD) ||
+			match($supportBlock->getTypeId()){
 				BlockTypeIds::NETHERRACK,
 				BlockTypeIds::SOUL_SAND,
 				BlockTypeIds::SOUL_SOIL => true,
@@ -47,19 +46,6 @@ private function canBeSupportedBy(Block $block) : bool{
 			};
 	}
 
-	public function onNearbyBlockChange() : void{
-		if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
-			$this->position->getWorld()->useBreakOn($this->position);
-		}
-	}
-
-	public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
-		if(!$this->canBeSupportedBy($blockReplace->getSide(Facing::DOWN))){
-			return false;
-		}
-		return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
-	}
-
 	public function hasEntityCollision() : bool{ return true; }
 
 	public function onEntityInside(Entity $entity) : bool{
diff --git a/src/block/WoodLikeBlockIdHelper.php b/src/block/WoodLikeBlockIdHelper.php
index f4cddb53b83..5f668608a81 100644
--- a/src/block/WoodLikeBlockIdHelper.php
+++ b/src/block/WoodLikeBlockIdHelper.php
@@ -30,7 +30,6 @@
 use pocketmine\block\utils\SaplingType;
 use pocketmine\block\utils\WoodType;
 use pocketmine\item\VanillaItems;
-use pocketmine\utils\AssumptionFailedError;
 
 /**
  * All wood-like blocks have different IDs for different wood types.
@@ -48,110 +47,103 @@
 final class WoodLikeBlockIdHelper{
 
 	public static function getPlanksIdentifier(WoodType $type) : BID{
-		return new BID(match($type->id()){
-			WoodType::OAK()->id() => Ids::OAK_PLANKS,
-			WoodType::SPRUCE()->id() => Ids::SPRUCE_PLANKS,
-			WoodType::BIRCH()->id() => Ids::BIRCH_PLANKS,
-			WoodType::JUNGLE()->id() => Ids::JUNGLE_PLANKS,
-			WoodType::ACACIA()->id() => Ids::ACACIA_PLANKS,
-			WoodType::DARK_OAK()->id() => Ids::DARK_OAK_PLANKS,
-			WoodType::MANGROVE()->id() => Ids::MANGROVE_PLANKS,
-			WoodType::CRIMSON()->id() => Ids::CRIMSON_PLANKS,
-			WoodType::WARPED()->id() => Ids::WARPED_PLANKS,
-			WoodType::CHERRY()->id() => Ids::CHERRY_PLANKS,
-			default => throw new AssumptionFailedError("All tree types should be covered")
+		return new BID(match($type){
+			WoodType::OAK => Ids::OAK_PLANKS,
+			WoodType::SPRUCE => Ids::SPRUCE_PLANKS,
+			WoodType::BIRCH => Ids::BIRCH_PLANKS,
+			WoodType::JUNGLE => Ids::JUNGLE_PLANKS,
+			WoodType::ACACIA => Ids::ACACIA_PLANKS,
+			WoodType::DARK_OAK => Ids::DARK_OAK_PLANKS,
+			WoodType::MANGROVE => Ids::MANGROVE_PLANKS,
+			WoodType::CRIMSON => Ids::CRIMSON_PLANKS,
+			WoodType::WARPED => Ids::WARPED_PLANKS,
+			WoodType::CHERRY => Ids::CHERRY_PLANKS,
 		});
 	}
 
 	public static function getFenceIdentifier(WoodType $type) : BID{
-		return new BID(match($type->id()){
-			WoodType::OAK()->id() => Ids::OAK_FENCE,
-			WoodType::SPRUCE()->id() => Ids::SPRUCE_FENCE,
-			WoodType::BIRCH()->id() => Ids::BIRCH_FENCE,
-			WoodType::JUNGLE()->id() => Ids::JUNGLE_FENCE,
-			WoodType::ACACIA()->id() => Ids::ACACIA_FENCE,
-			WoodType::DARK_OAK()->id() => Ids::DARK_OAK_FENCE,
-			WoodType::MANGROVE()->id() => Ids::MANGROVE_FENCE,
-			WoodType::CRIMSON()->id() => Ids::CRIMSON_FENCE,
-			WoodType::WARPED()->id() => Ids::WARPED_FENCE,
-			WoodType::CHERRY()->id() => Ids::CHERRY_FENCE,
-			default => throw new AssumptionFailedError("All tree types should be covered")
+		return new BID(match($type){
+			WoodType::OAK => Ids::OAK_FENCE,
+			WoodType::SPRUCE => Ids::SPRUCE_FENCE,
+			WoodType::BIRCH => Ids::BIRCH_FENCE,
+			WoodType::JUNGLE => Ids::JUNGLE_FENCE,
+			WoodType::ACACIA => Ids::ACACIA_FENCE,
+			WoodType::DARK_OAK => Ids::DARK_OAK_FENCE,
+			WoodType::MANGROVE => Ids::MANGROVE_FENCE,
+			WoodType::CRIMSON => Ids::CRIMSON_FENCE,
+			WoodType::WARPED => Ids::WARPED_FENCE,
+			WoodType::CHERRY => Ids::CHERRY_FENCE,
 		});
 	}
 
 	public static function getSlabIdentifier(WoodType $type) : BID{
-		return new BID(match($type->id()){
-			WoodType::OAK()->id() => Ids::OAK_SLAB,
-			WoodType::SPRUCE()->id() => Ids::SPRUCE_SLAB,
-			WoodType::BIRCH()->id() => Ids::BIRCH_SLAB,
-			WoodType::JUNGLE()->id() => Ids::JUNGLE_SLAB,
-			WoodType::ACACIA()->id() => Ids::ACACIA_SLAB,
-			WoodType::DARK_OAK()->id() => Ids::DARK_OAK_SLAB,
-			WoodType::MANGROVE()->id() => Ids::MANGROVE_SLAB,
-			WoodType::CRIMSON()->id() => Ids::CRIMSON_SLAB,
-			WoodType::WARPED()->id() => Ids::WARPED_SLAB,
-			WoodType::CHERRY()->id() => Ids::CHERRY_SLAB,
-			default => throw new AssumptionFailedError("All tree types should be covered")
+		return new BID(match($type){
+			WoodType::OAK => Ids::OAK_SLAB,
+			WoodType::SPRUCE => Ids::SPRUCE_SLAB,
+			WoodType::BIRCH => Ids::BIRCH_SLAB,
+			WoodType::JUNGLE => Ids::JUNGLE_SLAB,
+			WoodType::ACACIA => Ids::ACACIA_SLAB,
+			WoodType::DARK_OAK => Ids::DARK_OAK_SLAB,
+			WoodType::MANGROVE => Ids::MANGROVE_SLAB,
+			WoodType::CRIMSON => Ids::CRIMSON_SLAB,
+			WoodType::WARPED => Ids::WARPED_SLAB,
+			WoodType::CHERRY => Ids::CHERRY_SLAB,
 		});
 	}
 
 	public static function getLogIdentifier(WoodType $treeType) : BID{
-		return new BID(match($treeType->id()){
-			WoodType::OAK()->id() => Ids::OAK_LOG,
-			WoodType::SPRUCE()->id() => Ids::SPRUCE_LOG,
-			WoodType::BIRCH()->id() => Ids::BIRCH_LOG,
-			WoodType::JUNGLE()->id() => Ids::JUNGLE_LOG,
-			WoodType::ACACIA()->id() => Ids::ACACIA_LOG,
-			WoodType::DARK_OAK()->id() => Ids::DARK_OAK_LOG,
-			WoodType::MANGROVE()->id() => Ids::MANGROVE_LOG,
-			WoodType::CRIMSON()->id() => Ids::CRIMSON_STEM,
-			WoodType::WARPED()->id() => Ids::WARPED_STEM,
-			WoodType::CHERRY()->id() => Ids::CHERRY_LOG,
-			default => throw new AssumptionFailedError("All tree types should be covered")
+		return new BID(match($treeType){
+			WoodType::OAK => Ids::OAK_LOG,
+			WoodType::SPRUCE => Ids::SPRUCE_LOG,
+			WoodType::BIRCH => Ids::BIRCH_LOG,
+			WoodType::JUNGLE => Ids::JUNGLE_LOG,
+			WoodType::ACACIA => Ids::ACACIA_LOG,
+			WoodType::DARK_OAK => Ids::DARK_OAK_LOG,
+			WoodType::MANGROVE => Ids::MANGROVE_LOG,
+			WoodType::CRIMSON => Ids::CRIMSON_STEM,
+			WoodType::WARPED => Ids::WARPED_STEM,
+			WoodType::CHERRY => Ids::CHERRY_LOG,
 		});
 	}
 
 	public static function getAllSidedLogIdentifier(WoodType $treeType) : BID{
-		return new BID(match($treeType->id()){
-			WoodType::OAK()->id() => Ids::OAK_WOOD,
-			WoodType::SPRUCE()->id() => Ids::SPRUCE_WOOD,
-			WoodType::BIRCH()->id() => Ids::BIRCH_WOOD,
-			WoodType::JUNGLE()->id() => Ids::JUNGLE_WOOD,
-			WoodType::ACACIA()->id() => Ids::ACACIA_WOOD,
-			WoodType::DARK_OAK()->id() => Ids::DARK_OAK_WOOD,
-			WoodType::MANGROVE()->id() => Ids::MANGROVE_WOOD,
-			WoodType::CRIMSON()->id() => Ids::CRIMSON_HYPHAE,
-			WoodType::WARPED()->id() => Ids::WARPED_HYPHAE,
-			WoodType::CHERRY()->id() => Ids::CHERRY_WOOD,
-			default => throw new AssumptionFailedError("All tree types should be covered")
+		return new BID(match($treeType){
+			WoodType::OAK => Ids::OAK_WOOD,
+			WoodType::SPRUCE => Ids::SPRUCE_WOOD,
+			WoodType::BIRCH => Ids::BIRCH_WOOD,
+			WoodType::JUNGLE => Ids::JUNGLE_WOOD,
+			WoodType::ACACIA => Ids::ACACIA_WOOD,
+			WoodType::DARK_OAK => Ids::DARK_OAK_WOOD,
+			WoodType::MANGROVE => Ids::MANGROVE_WOOD,
+			WoodType::CRIMSON => Ids::CRIMSON_HYPHAE,
+			WoodType::WARPED => Ids::WARPED_HYPHAE,
+			WoodType::CHERRY => Ids::CHERRY_WOOD,
 		});
 	}
 
 	public static function getLeavesIdentifier(LeavesType $leavesType) : BID{
-		return new BID(match($leavesType->id()){
-			LeavesType::OAK()->id() => Ids::OAK_LEAVES,
-			LeavesType::SPRUCE()->id() => Ids::SPRUCE_LEAVES,
-			LeavesType::BIRCH()->id() => Ids::BIRCH_LEAVES,
-			LeavesType::JUNGLE()->id() => Ids::JUNGLE_LEAVES,
-			LeavesType::ACACIA()->id() => Ids::ACACIA_LEAVES,
-			LeavesType::DARK_OAK()->id() => Ids::DARK_OAK_LEAVES,
-			LeavesType::MANGROVE()->id() => Ids::MANGROVE_LEAVES,
-			LeavesType::AZALEA()->id() => Ids::AZALEA_LEAVES,
-			LeavesType::FLOWERING_AZALEA()->id() => Ids::FLOWERING_AZALEA_LEAVES,
-			LeavesType::CHERRY()->id() => Ids::CHERRY_LEAVES,
-			default => throw new AssumptionFailedError("All leaves types should be covered")
+		return new BID(match($leavesType){
+			LeavesType::OAK => Ids::OAK_LEAVES,
+			LeavesType::SPRUCE => Ids::SPRUCE_LEAVES,
+			LeavesType::BIRCH => Ids::BIRCH_LEAVES,
+			LeavesType::JUNGLE => Ids::JUNGLE_LEAVES,
+			LeavesType::ACACIA => Ids::ACACIA_LEAVES,
+			LeavesType::DARK_OAK => Ids::DARK_OAK_LEAVES,
+			LeavesType::MANGROVE => Ids::MANGROVE_LEAVES,
+			LeavesType::AZALEA => Ids::AZALEA_LEAVES,
+			LeavesType::FLOWERING_AZALEA => Ids::FLOWERING_AZALEA_LEAVES,
+			LeavesType::CHERRY => Ids::CHERRY_LEAVES,
 		});
 	}
 
 	public static function getSaplingIdentifier(SaplingType $treeType) : BID{
-		return new BID(match($treeType->id()){
-			SaplingType::OAK()->id() => Ids::OAK_SAPLING,
-			SaplingType::SPRUCE()->id() => Ids::SPRUCE_SAPLING,
-			SaplingType::BIRCH()->id() => Ids::BIRCH_SAPLING,
-			SaplingType::JUNGLE()->id() => Ids::JUNGLE_SAPLING,
-			SaplingType::ACACIA()->id() => Ids::ACACIA_SAPLING,
-			SaplingType::DARK_OAK()->id() => Ids::DARK_OAK_SAPLING,
-			default => throw new AssumptionFailedError("All tree types should be covered")
+		return new BID(match($treeType){
+			SaplingType::OAK => Ids::OAK_SAPLING,
+			SaplingType::SPRUCE => Ids::SPRUCE_SAPLING,
+			SaplingType::BIRCH => Ids::BIRCH_SAPLING,
+			SaplingType::JUNGLE => Ids::JUNGLE_SAPLING,
+			SaplingType::ACACIA => Ids::ACACIA_SAPLING,
+			SaplingType::DARK_OAK => Ids::DARK_OAK_SAPLING,
 		});
 	}
 
@@ -160,164 +152,112 @@ public static function getSaplingIdentifier(SaplingType $treeType) : BID{
 	 * @phpstan-return array{BID, BID, \Closure() : \pocketmine\item\Item}
 	 */
 	public static function getSignInfo(WoodType $treeType) : array{
-		switch($treeType->id()){
-			case WoodType::OAK()->id():
-				return [
-					new BID(Ids::OAK_SIGN, TileSign::class),
-					new BID(Ids::OAK_WALL_SIGN, TileSign::class),
-					fn() => VanillaItems::OAK_SIGN()
-				];
-			case WoodType::SPRUCE()->id():
-				return [
-					new BID(Ids::SPRUCE_SIGN, TileSign::class),
-					new BID(Ids::SPRUCE_WALL_SIGN, TileSign::class),
-					fn() => VanillaItems::SPRUCE_SIGN()
-				];
-			case WoodType::BIRCH()->id():
-				return [
-					new BID(Ids::BIRCH_SIGN, TileSign::class),
-					new BID(Ids::BIRCH_WALL_SIGN, TileSign::class),
-					fn() => VanillaItems::BIRCH_SIGN()
-				];
-			case WoodType::JUNGLE()->id():
-				return [
-					new BID(Ids::JUNGLE_SIGN, TileSign::class),
-					new BID(Ids::JUNGLE_WALL_SIGN, TileSign::class),
-					fn() => VanillaItems::JUNGLE_SIGN()
-				];
-			case WoodType::ACACIA()->id():
-				return [
-					new BID(Ids::ACACIA_SIGN, TileSign::class),
-					new BID(Ids::ACACIA_WALL_SIGN, TileSign::class),
-					fn() => VanillaItems::ACACIA_SIGN()
-				];
-			case WoodType::DARK_OAK()->id():
-				return [
-					new BID(Ids::DARK_OAK_SIGN, TileSign::class),
-					new BID(Ids::DARK_OAK_WALL_SIGN, TileSign::class),
-					fn() => VanillaItems::DARK_OAK_SIGN()
-				];
-			case WoodType::MANGROVE()->id():
-				return [
-					new BID(Ids::MANGROVE_SIGN, TileSign::class),
-					new BID(Ids::MANGROVE_WALL_SIGN, TileSign::class),
-					fn() => VanillaItems::MANGROVE_SIGN()
-				];
-			case WoodType::CRIMSON()->id():
-				return [
-					new BID(Ids::CRIMSON_SIGN, TileSign::class),
-					new BID(Ids::CRIMSON_WALL_SIGN, TileSign::class),
-					fn() => VanillaItems::CRIMSON_SIGN()
-				];
-			case WoodType::WARPED()->id():
-				return [
-					new BID(Ids::WARPED_SIGN, TileSign::class),
-					new BID(Ids::WARPED_WALL_SIGN, TileSign::class),
-					fn() => VanillaItems::WARPED_SIGN()
-				];
-			case WoodType::CHERRY()->id():
-				return [
-					new BID(Ids::CHERRY_SIGN, TileSign::class),
-					new BID(Ids::CHERRY_WALL_SIGN, TileSign::class),
-					fn() => VanillaItems::CHERRY_SIGN()
-				];
-		}
-		throw new AssumptionFailedError("Switch should cover all wood types");
+		$make = fn(int $floorId, int $wallId, \Closure $getItem) => [
+			new BID($floorId, TileSign::class),
+			new BID($wallId, TileSign::class),
+			$getItem
+		];
+		return match($treeType){
+			WoodType::OAK => $make(Ids::OAK_SIGN, Ids::OAK_WALL_SIGN, fn() => VanillaItems::OAK_SIGN()),
+			WoodType::SPRUCE => $make(Ids::SPRUCE_SIGN, Ids::SPRUCE_WALL_SIGN, fn() => VanillaItems::SPRUCE_SIGN()),
+			WoodType::BIRCH => $make(Ids::BIRCH_SIGN, Ids::BIRCH_WALL_SIGN, fn() => VanillaItems::BIRCH_SIGN()),
+			WoodType::JUNGLE => $make(Ids::JUNGLE_SIGN, Ids::JUNGLE_WALL_SIGN, fn() => VanillaItems::JUNGLE_SIGN()),
+			WoodType::ACACIA => $make(Ids::ACACIA_SIGN, Ids::ACACIA_WALL_SIGN, fn() => VanillaItems::ACACIA_SIGN()),
+			WoodType::DARK_OAK => $make(Ids::DARK_OAK_SIGN, Ids::DARK_OAK_WALL_SIGN, fn() => VanillaItems::DARK_OAK_SIGN()),
+			WoodType::MANGROVE => $make(Ids::MANGROVE_SIGN, Ids::MANGROVE_WALL_SIGN, fn() => VanillaItems::MANGROVE_SIGN()),
+			WoodType::CRIMSON => $make(Ids::CRIMSON_SIGN, Ids::CRIMSON_WALL_SIGN, fn() => VanillaItems::CRIMSON_SIGN()),
+			WoodType::WARPED => $make(Ids::WARPED_SIGN, Ids::WARPED_WALL_SIGN, fn() => VanillaItems::WARPED_SIGN()),
+			WoodType::CHERRY => $make(Ids::CHERRY_SIGN, Ids::CHERRY_WALL_SIGN, fn() => VanillaItems::CHERRY_SIGN()),
+		};
 	}
 
 	public static function getTrapdoorIdentifier(WoodType $treeType) : BlockIdentifier{
-		return new BID(match($treeType->id()){
-			WoodType::OAK()->id() => Ids::OAK_TRAPDOOR,
-			WoodType::SPRUCE()->id() => Ids::SPRUCE_TRAPDOOR,
-			WoodType::BIRCH()->id() => Ids::BIRCH_TRAPDOOR,
-			WoodType::JUNGLE()->id() => Ids::JUNGLE_TRAPDOOR,
-			WoodType::ACACIA()->id() => Ids::ACACIA_TRAPDOOR,
-			WoodType::DARK_OAK()->id() => Ids::DARK_OAK_TRAPDOOR,
-			WoodType::MANGROVE()->id() => Ids::MANGROVE_TRAPDOOR,
-			WoodType::CRIMSON()->id() => Ids::CRIMSON_TRAPDOOR,
-			WoodType::WARPED()->id() => Ids::WARPED_TRAPDOOR,
-			WoodType::CHERRY()->id() => Ids::CHERRY_TRAPDOOR,
-			default => throw new AssumptionFailedError("All wood types should be covered")
+		return new BID(match($treeType){
+			WoodType::OAK => Ids::OAK_TRAPDOOR,
+			WoodType::SPRUCE => Ids::SPRUCE_TRAPDOOR,
+			WoodType::BIRCH => Ids::BIRCH_TRAPDOOR,
+			WoodType::JUNGLE => Ids::JUNGLE_TRAPDOOR,
+			WoodType::ACACIA => Ids::ACACIA_TRAPDOOR,
+			WoodType::DARK_OAK => Ids::DARK_OAK_TRAPDOOR,
+			WoodType::MANGROVE => Ids::MANGROVE_TRAPDOOR,
+			WoodType::CRIMSON => Ids::CRIMSON_TRAPDOOR,
+			WoodType::WARPED => Ids::WARPED_TRAPDOOR,
+			WoodType::CHERRY => Ids::CHERRY_TRAPDOOR,
 		});
 	}
 
 	public static function getButtonIdentifier(WoodType $treeType) : BlockIdentifier{
-		return new BID(match($treeType->id()){
-			WoodType::OAK()->id() => Ids::OAK_BUTTON,
-			WoodType::SPRUCE()->id() => Ids::SPRUCE_BUTTON,
-			WoodType::BIRCH()->id() => Ids::BIRCH_BUTTON,
-			WoodType::JUNGLE()->id() => Ids::JUNGLE_BUTTON,
-			WoodType::ACACIA()->id() => Ids::ACACIA_BUTTON,
-			WoodType::DARK_OAK()->id() => Ids::DARK_OAK_BUTTON,
-			WoodType::MANGROVE()->id() => Ids::MANGROVE_BUTTON,
-			WoodType::CRIMSON()->id() => Ids::CRIMSON_BUTTON,
-			WoodType::WARPED()->id() => Ids::WARPED_BUTTON,
-			WoodType::CHERRY()->id() => Ids::CHERRY_BUTTON,
-			default => throw new AssumptionFailedError("All wood types should be covered")
+		return new BID(match($treeType){
+			WoodType::OAK => Ids::OAK_BUTTON,
+			WoodType::SPRUCE => Ids::SPRUCE_BUTTON,
+			WoodType::BIRCH => Ids::BIRCH_BUTTON,
+			WoodType::JUNGLE => Ids::JUNGLE_BUTTON,
+			WoodType::ACACIA => Ids::ACACIA_BUTTON,
+			WoodType::DARK_OAK => Ids::DARK_OAK_BUTTON,
+			WoodType::MANGROVE => Ids::MANGROVE_BUTTON,
+			WoodType::CRIMSON => Ids::CRIMSON_BUTTON,
+			WoodType::WARPED => Ids::WARPED_BUTTON,
+			WoodType::CHERRY => Ids::CHERRY_BUTTON,
 		});
 	}
 
 	public static function getPressurePlateIdentifier(WoodType $treeType) : BlockIdentifier{
-		return new BID(match($treeType->id()){
-			WoodType::OAK()->id() => Ids::OAK_PRESSURE_PLATE,
-			WoodType::SPRUCE()->id() => Ids::SPRUCE_PRESSURE_PLATE,
-			WoodType::BIRCH()->id() => Ids::BIRCH_PRESSURE_PLATE,
-			WoodType::JUNGLE()->id() => Ids::JUNGLE_PRESSURE_PLATE,
-			WoodType::ACACIA()->id() => Ids::ACACIA_PRESSURE_PLATE,
-			WoodType::DARK_OAK()->id() => Ids::DARK_OAK_PRESSURE_PLATE,
-			WoodType::MANGROVE()->id() => Ids::MANGROVE_PRESSURE_PLATE,
-			WoodType::CRIMSON()->id() => Ids::CRIMSON_PRESSURE_PLATE,
-			WoodType::WARPED()->id() => Ids::WARPED_PRESSURE_PLATE,
-			WoodType::CHERRY()->id() => Ids::CHERRY_PRESSURE_PLATE,
-			default => throw new AssumptionFailedError("All wood types should be covered")
+		return new BID(match($treeType){
+			WoodType::OAK => Ids::OAK_PRESSURE_PLATE,
+			WoodType::SPRUCE => Ids::SPRUCE_PRESSURE_PLATE,
+			WoodType::BIRCH => Ids::BIRCH_PRESSURE_PLATE,
+			WoodType::JUNGLE => Ids::JUNGLE_PRESSURE_PLATE,
+			WoodType::ACACIA => Ids::ACACIA_PRESSURE_PLATE,
+			WoodType::DARK_OAK => Ids::DARK_OAK_PRESSURE_PLATE,
+			WoodType::MANGROVE => Ids::MANGROVE_PRESSURE_PLATE,
+			WoodType::CRIMSON => Ids::CRIMSON_PRESSURE_PLATE,
+			WoodType::WARPED => Ids::WARPED_PRESSURE_PLATE,
+			WoodType::CHERRY => Ids::CHERRY_PRESSURE_PLATE,
 		});
 	}
 
 	public static function getDoorIdentifier(WoodType $treeType) : BlockIdentifier{
-		return new BID(match($treeType->id()){
-			WoodType::OAK()->id() => Ids::OAK_DOOR,
-			WoodType::SPRUCE()->id() => Ids::SPRUCE_DOOR,
-			WoodType::BIRCH()->id() => Ids::BIRCH_DOOR,
-			WoodType::JUNGLE()->id() => Ids::JUNGLE_DOOR,
-			WoodType::ACACIA()->id() => Ids::ACACIA_DOOR,
-			WoodType::DARK_OAK()->id() => Ids::DARK_OAK_DOOR,
-			WoodType::MANGROVE()->id() => Ids::MANGROVE_DOOR,
-			WoodType::CRIMSON()->id() => Ids::CRIMSON_DOOR,
-			WoodType::WARPED()->id() => Ids::WARPED_DOOR,
-			WoodType::CHERRY()->id() => Ids::CHERRY_DOOR,
-			default => throw new AssumptionFailedError("All wood types should be covered")
+		return new BID(match($treeType){
+			WoodType::OAK => Ids::OAK_DOOR,
+			WoodType::SPRUCE => Ids::SPRUCE_DOOR,
+			WoodType::BIRCH => Ids::BIRCH_DOOR,
+			WoodType::JUNGLE => Ids::JUNGLE_DOOR,
+			WoodType::ACACIA => Ids::ACACIA_DOOR,
+			WoodType::DARK_OAK => Ids::DARK_OAK_DOOR,
+			WoodType::MANGROVE => Ids::MANGROVE_DOOR,
+			WoodType::CRIMSON => Ids::CRIMSON_DOOR,
+			WoodType::WARPED => Ids::WARPED_DOOR,
+			WoodType::CHERRY => Ids::CHERRY_DOOR,
 		});
 	}
 
 	public static function getFenceGateIdentifier(WoodType $treeType) : BlockIdentifier{
-		return new BID(match($treeType->id()){
-			WoodType::OAK()->id() => Ids::OAK_FENCE_GATE,
-			WoodType::SPRUCE()->id() => Ids::SPRUCE_FENCE_GATE,
-			WoodType::BIRCH()->id() => Ids::BIRCH_FENCE_GATE,
-			WoodType::JUNGLE()->id() => Ids::JUNGLE_FENCE_GATE,
-			WoodType::ACACIA()->id() => Ids::ACACIA_FENCE_GATE,
-			WoodType::DARK_OAK()->id() => Ids::DARK_OAK_FENCE_GATE,
-			WoodType::MANGROVE()->id() => Ids::MANGROVE_FENCE_GATE,
-			WoodType::CRIMSON()->id() => Ids::CRIMSON_FENCE_GATE,
-			WoodType::WARPED()->id() => Ids::WARPED_FENCE_GATE,
-			WoodType::CHERRY()->id() => Ids::CHERRY_FENCE_GATE,
-			default => throw new AssumptionFailedError("All wood types should be covered")
+		return new BID(match($treeType){
+			WoodType::OAK => Ids::OAK_FENCE_GATE,
+			WoodType::SPRUCE => Ids::SPRUCE_FENCE_GATE,
+			WoodType::BIRCH => Ids::BIRCH_FENCE_GATE,
+			WoodType::JUNGLE => Ids::JUNGLE_FENCE_GATE,
+			WoodType::ACACIA => Ids::ACACIA_FENCE_GATE,
+			WoodType::DARK_OAK => Ids::DARK_OAK_FENCE_GATE,
+			WoodType::MANGROVE => Ids::MANGROVE_FENCE_GATE,
+			WoodType::CRIMSON => Ids::CRIMSON_FENCE_GATE,
+			WoodType::WARPED => Ids::WARPED_FENCE_GATE,
+			WoodType::CHERRY => Ids::CHERRY_FENCE_GATE,
 		});
 	}
 
 	public static function getStairsIdentifier(WoodType $treeType) : BlockIdentifier{
-		return new BID(match($treeType->id()){
-			WoodType::OAK()->id() => Ids::OAK_STAIRS,
-			WoodType::SPRUCE()->id() => Ids::SPRUCE_STAIRS,
-			WoodType::BIRCH()->id() => Ids::BIRCH_STAIRS,
-			WoodType::JUNGLE()->id() => Ids::JUNGLE_STAIRS,
-			WoodType::ACACIA()->id() => Ids::ACACIA_STAIRS,
-			WoodType::DARK_OAK()->id() => Ids::DARK_OAK_STAIRS,
-			WoodType::MANGROVE()->id() => Ids::MANGROVE_STAIRS,
-			WoodType::CRIMSON()->id() => Ids::CRIMSON_STAIRS,
-			WoodType::WARPED()->id() => Ids::WARPED_STAIRS,
-			WoodType::CHERRY()->id() => Ids::CHERRY_STAIRS,
-			default => throw new AssumptionFailedError("All wood types should be covered")
+		return new BID(match($treeType){
+			WoodType::OAK => Ids::OAK_STAIRS,
+			WoodType::SPRUCE => Ids::SPRUCE_STAIRS,
+			WoodType::BIRCH => Ids::BIRCH_STAIRS,
+			WoodType::JUNGLE => Ids::JUNGLE_STAIRS,
+			WoodType::ACACIA => Ids::ACACIA_STAIRS,
+			WoodType::DARK_OAK => Ids::DARK_OAK_STAIRS,
+			WoodType::MANGROVE => Ids::MANGROVE_STAIRS,
+			WoodType::CRIMSON => Ids::CRIMSON_STAIRS,
+			WoodType::WARPED => Ids::WARPED_STAIRS,
+			WoodType::CHERRY => Ids::CHERRY_STAIRS,
 		});
 	}
 }
diff --git a/src/block/Wool.php b/src/block/Wool.php
index 2cc2b75358c..0b008ac042f 100644
--- a/src/block/Wool.php
+++ b/src/block/Wool.php
@@ -24,16 +24,10 @@
 namespace pocketmine\block;
 
 use pocketmine\block\utils\ColoredTrait;
-use pocketmine\block\utils\DyeColor;
 
 class Wool extends Opaque{
 	use ColoredTrait;
 
-	public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
-		$this->color = DyeColor::WHITE();
-		parent::__construct($idInfo, $name, $typeInfo);
-	}
-
 	public function getFlameEncouragement() : int{
 		return 30;
 	}
diff --git a/src/block/tile/Banner.php b/src/block/tile/Banner.php
index 191d4c8a522..08a560707b2 100644
--- a/src/block/tile/Banner.php
+++ b/src/block/tile/Banner.php
@@ -27,11 +27,9 @@
 use pocketmine\block\utils\DyeColor;
 use pocketmine\data\bedrock\BannerPatternTypeIdMap;
 use pocketmine\data\bedrock\DyeColorIdMap;
-use pocketmine\math\Vector3;
 use pocketmine\nbt\tag\CompoundTag;
 use pocketmine\nbt\tag\IntTag;
 use pocketmine\nbt\tag\ListTag;
-use pocketmine\world\World;
 
 /**
  * @deprecated
@@ -44,7 +42,7 @@ class Banner extends Spawnable{
 	public const TAG_PATTERN_COLOR = "Color";
 	public const TAG_PATTERN_NAME = "Pattern";
 
-	private DyeColor $baseColor;
+	private DyeColor $baseColor = DyeColor::BLACK;
 
 	/**
 	 * @var BannerPatternLayer[]
@@ -52,11 +50,6 @@ class Banner extends Spawnable{
 	 */
 	private array $patterns = [];
 
-	public function __construct(World $world, Vector3 $pos){
-		$this->baseColor = DyeColor::BLACK();
-		parent::__construct($world, $pos);
-	}
-
 	public function readSaveData(CompoundTag $nbt) : void{
 		$colorIdMap = DyeColorIdMap::getInstance();
 		if(
@@ -65,7 +58,7 @@ public function readSaveData(CompoundTag $nbt) : void{
 		){
 			$this->baseColor = $baseColor;
 		}else{
-			$this->baseColor = DyeColor::BLACK(); //TODO: this should be an error
+			$this->baseColor = DyeColor::BLACK; //TODO: this should be an error
 		}
 
 		$patternTypeIdMap = BannerPatternTypeIdMap::getInstance();
@@ -74,7 +67,7 @@ public function readSaveData(CompoundTag $nbt) : void{
 		if($patterns !== null){
 			/** @var CompoundTag $pattern */
 			foreach($patterns as $pattern){
-				$patternColor = $colorIdMap->fromInvertedId($pattern->getInt(self::TAG_PATTERN_COLOR)) ?? DyeColor::BLACK(); //TODO: missing pattern colour should be an error
+				$patternColor = $colorIdMap->fromInvertedId($pattern->getInt(self::TAG_PATTERN_COLOR)) ?? DyeColor::BLACK; //TODO: missing pattern colour should be an error
 				$patternType = $patternTypeIdMap->fromId($pattern->getString(self::TAG_PATTERN_NAME));
 				if($patternType === null){
 					continue; //TODO: this should be an error, but right now we don't have the setup to deal with it
diff --git a/src/block/tile/Bed.php b/src/block/tile/Bed.php
index 847bcca1fdf..17a0c8d9d33 100644
--- a/src/block/tile/Bed.php
+++ b/src/block/tile/Bed.php
@@ -25,20 +25,13 @@
 
 use pocketmine\block\utils\DyeColor;
 use pocketmine\data\bedrock\DyeColorIdMap;
-use pocketmine\math\Vector3;
 use pocketmine\nbt\tag\ByteTag;
 use pocketmine\nbt\tag\CompoundTag;
-use pocketmine\world\World;
 
 class Bed extends Spawnable{
 	public const TAG_COLOR = "color";
 
-	private DyeColor $color;
-
-	public function __construct(World $world, Vector3 $pos){
-		$this->color = DyeColor::RED();
-		parent::__construct($world, $pos);
-	}
+	private DyeColor $color = DyeColor::RED;
 
 	public function getColor() : DyeColor{
 		return $this->color;
@@ -55,7 +48,7 @@ public function readSaveData(CompoundTag $nbt) : void{
 		){
 			$this->color = $color;
 		}else{
-			$this->color = DyeColor::RED(); //TODO: this should be an error, but we don't have the systems to handle it yet
+			$this->color = DyeColor::RED; //TODO: this should be an error, but we don't have the systems to handle it yet
 		}
 	}
 
diff --git a/src/block/tile/BlastFurnace.php b/src/block/tile/BlastFurnace.php
index e6e23c88c3b..1356e32bf4f 100644
--- a/src/block/tile/BlastFurnace.php
+++ b/src/block/tile/BlastFurnace.php
@@ -27,6 +27,6 @@
 
 class BlastFurnace extends Furnace{
 	public function getFurnaceType() : FurnaceType{
-		return FurnaceType::BLAST_FURNACE();
+		return FurnaceType::BLAST_FURNACE;
 	}
 }
diff --git a/src/block/tile/ChiseledBookshelf.php b/src/block/tile/ChiseledBookshelf.php
new file mode 100644
index 00000000000..f9d61e7eb6d
--- /dev/null
+++ b/src/block/tile/ChiseledBookshelf.php
@@ -0,0 +1,117 @@
+<?php
+
+/*
+ *
+ *  ____            _        _   __  __ _                  __  __ ____
+ * |  _ \ ___   ___| | _____| |_|  \/  (_)_ __   ___      |  \/  |  _ \
+ * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
+ * |  __/ (_) | (__|   <  __/ |_| |  | | | | | |  __/_____| |  | |  __/
+ * |_|   \___/ \___|_|\_\___|\__|_|  |_|_|_| |_|\___|     |_|  |_|_|
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * @author PocketMine Team
+ * @link http://www.pocketmine.net/
+ *
+ *
+ */
+
+declare(strict_types=1);
+
+namespace pocketmine\block\tile;
+
+use pocketmine\block\utils\ChiseledBookshelfSlot;
+use pocketmine\data\bedrock\item\SavedItemData;
+use pocketmine\data\bedrock\item\SavedItemStackData;
+use pocketmine\data\SavedDataLoadingException;
+use pocketmine\inventory\SimpleInventory;
+use pocketmine\item\Item;
+use pocketmine\math\Vector3;
+use pocketmine\nbt\NBT;
+use pocketmine\nbt\tag\CompoundTag;
+use pocketmine\nbt\tag\ListTag;
+use pocketmine\nbt\tag\StringTag;
+use pocketmine\world\World;
+use function count;
+
+class ChiseledBookshelf extends Tile implements Container{
+	use ContainerTrait;
+
+	private SimpleInventory $inventory;
+
+	public function __construct(World $world, Vector3 $pos){
+		parent::__construct($world, $pos);
+		$this->inventory = new SimpleInventory(count(ChiseledBookshelfSlot::cases()));
+	}
+
+	public function getInventory() : SimpleInventory{
+		return $this->inventory;
+	}
+
+	public function getRealInventory() : SimpleInventory{
+		return $this->inventory;
+	}
+
+	public function readSaveData(CompoundTag $nbt) : void{
+		$this->loadItems($nbt);
+	}
+
+	public function writeSaveData(CompoundTag $nbt) : void{
+		$this->saveItems($nbt);
+	}
+
+	protected function loadItems(CompoundTag $tag) : void{
+		if(($inventoryTag = $tag->getTag(Container::TAG_ITEMS)) instanceof ListTag && $inventoryTag->getTagType() === NBT::TAG_Compound){
+			$inventory = $this->getRealInventory();
+			$listeners = $inventory->getListeners()->toArray();
+			$inventory->getListeners()->remove(...$listeners); //prevent any events being fired by initialization
+
+			$newContents = [];
+			/** @var CompoundTag $itemNBT */
+			foreach($inventoryTag as $slot => $itemNBT){
+				try{
+					$count = $itemNBT->getByte(SavedItemStackData::TAG_COUNT);
+					if($count === 0){
+						continue;
+					}
+					$newContents[$slot] = Item::nbtDeserialize($itemNBT);
+				}catch(SavedDataLoadingException $e){
+					//TODO: not the best solution
+					\GlobalLogger::get()->logException($e);
+					continue;
+				}
+			}
+			$inventory->setContents($newContents);
+
+			$inventory->getListeners()->add(...$listeners);
+		}
+
+		if(($lockTag = $tag->getTag(Container::TAG_LOCK)) instanceof StringTag){
+			$this->lock = $lockTag->getValue();
+		}
+	}
+
+	protected function saveItems(CompoundTag $tag) : void{
+		$items = [];
+		foreach($this->getRealInventory()->getContents(true) as $slot => $item){
+			if($item->isNull()){
+				$items[$slot] = CompoundTag::create()
+					->setByte(SavedItemStackData::TAG_COUNT, 0)
+					->setShort(SavedItemData::TAG_DAMAGE, 0)
+					->setString(SavedItemData::TAG_NAME, "")
+					->setByte(SavedItemStackData::TAG_WAS_PICKED_UP, 0);
+			}else{
+				$items[$slot] = $item->nbtSerialize();
+			}
+		}
+
+		$tag->setTag(Container::TAG_ITEMS, new ListTag($items, NBT::TAG_Compound));
+
+		if($this->lock !== null){
+			$tag->setString(Container::TAG_LOCK, $this->lock);
+		}
+	}
+}
diff --git a/src/block/tile/MobHead.php b/src/block/tile/MobHead.php
index 70a199bf67b..be7c0ac2bc2 100644
--- a/src/block/tile/MobHead.php
+++ b/src/block/tile/MobHead.php
@@ -26,10 +26,8 @@
 use pocketmine\block\utils\MobHeadType;
 use pocketmine\data\bedrock\MobHeadTypeIdMap;
 use pocketmine\data\SavedDataLoadingException;
-use pocketmine\math\Vector3;
 use pocketmine\nbt\tag\ByteTag;
 use pocketmine\nbt\tag\CompoundTag;
-use pocketmine\world\World;
 
 /**
  * @deprecated
@@ -42,14 +40,9 @@ class MobHead extends Spawnable{
 	private const TAG_MOUTH_MOVING = "MouthMoving"; //TAG_Byte
 	private const TAG_MOUTH_TICK_COUNT = "MouthTickCount"; //TAG_Int
 
-	private MobHeadType $mobHeadType;
+	private MobHeadType $mobHeadType = MobHeadType::SKELETON;
 	private int $rotation = 0;
 
-	public function __construct(World $world, Vector3 $pos){
-		$this->mobHeadType = MobHeadType::SKELETON();
-		parent::__construct($world, $pos);
-	}
-
 	public function readSaveData(CompoundTag $nbt) : void{
 		if(($skullTypeTag = $nbt->getTag(self::TAG_SKULL_TYPE)) instanceof ByteTag){
 			$mobHeadType = MobHeadTypeIdMap::getInstance()->fromId($skullTypeTag->getValue());
diff --git a/src/block/tile/NormalFurnace.php b/src/block/tile/NormalFurnace.php
index 9580920e201..6aa61b8ce72 100644
--- a/src/block/tile/NormalFurnace.php
+++ b/src/block/tile/NormalFurnace.php
@@ -27,6 +27,6 @@
 
 class NormalFurnace extends Furnace{
 	public function getFurnaceType() : FurnaceType{
-		return FurnaceType::FURNACE();
+		return FurnaceType::FURNACE;
 	}
 }
diff --git a/src/block/tile/Smoker.php b/src/block/tile/Smoker.php
index 4b5a017336b..824eeedd28c 100644
--- a/src/block/tile/Smoker.php
+++ b/src/block/tile/Smoker.php
@@ -27,6 +27,6 @@
 
 class Smoker extends Furnace{
 	public function getFurnaceType() : FurnaceType{
-		return FurnaceType::SMOKER();
+		return FurnaceType::SMOKER;
 	}
 }
diff --git a/src/block/tile/TileFactory.php b/src/block/tile/TileFactory.php
index b3abb1315a6..6e87b72acb9 100644
--- a/src/block/tile/TileFactory.php
+++ b/src/block/tile/TileFactory.php
@@ -59,6 +59,7 @@ public function __construct(){
 		$this->register(BrewingStand::class, ["BrewingStand", "minecraft:brewing_stand"]);
 		$this->register(Cauldron::class, ["Cauldron", "minecraft:cauldron"]);
 		$this->register(Chest::class, ["Chest", "minecraft:chest"]);
+		$this->register(ChiseledBookshelf::class, ["ChiseledBookshelf", "minecraft:chiseled_bookshelf"]);
 		$this->register(Comparator::class, ["Comparator", "minecraft:comparator"]);
 		$this->register(DaylightSensor::class, ["DaylightDetector", "minecraft:daylight_detector"]);
 		$this->register(EnchantTable::class, ["EnchantTable", "minecraft:enchanting_table"]);
diff --git a/src/block/utils/AgeableTrait.php b/src/block/utils/AgeableTrait.php
new file mode 100644
index 00000000000..dc1369c8760
--- /dev/null
+++ b/src/block/utils/AgeableTrait.php
@@ -0,0 +1,51 @@
+<?php
+
+/*
+ *
+ *  ____            _        _   __  __ _                  __  __ ____
+ * |  _ \ ___   ___| | _____| |_|  \/  (_)_ __   ___      |  \/  |  _ \
+ * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
+ * |  __/ (_) | (__|   <  __/ |_| |  | | | | | |  __/_____| |  | |  __/
+ * |_|   \___/ \___|_|\_\___|\__|_|  |_|_|_| |_|\___|     |_|  |_|_|
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * @author PocketMine Team
+ * @link http://www.pocketmine.net/
+ *
+ *
+ */
+
+declare(strict_types=1);
+
+namespace pocketmine\block\utils;
+
+use pocketmine\data\runtime\RuntimeDataDescriber;
+
+/**
+ * This trait is used for blocks that have an age property.
+ * Need to add to the block the constant MAX_AGE.
+ */
+trait AgeableTrait{
+	protected int $age = 0;
+
+	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
+		$w->boundedIntAuto(0, self::MAX_AGE, $this->age);
+	}
+
+	public function getAge() : int{ return $this->age; }
+
+	/**
+	 * @return $this
+	 */
+	public function setAge(int $age) : self{
+		if($age < 0 || $age > self::MAX_AGE){
+			throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
+		}
+		$this->age = $age;
+		return $this;
+	}
+}
diff --git a/src/block/utils/AmethystTrait.php b/src/block/utils/AmethystTrait.php
new file mode 100644
index 00000000000..e581d3517dc
--- /dev/null
+++ b/src/block/utils/AmethystTrait.php
@@ -0,0 +1,40 @@
+<?php
+
+/*
+ *
+ *  ____            _        _   __  __ _                  __  __ ____
+ * |  _ \ ___   ___| | _____| |_|  \/  (_)_ __   ___      |  \/  |  _ \
+ * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
+ * |  __/ (_) | (__|   <  __/ |_| |  | | | | | |  __/_____| |  | |  __/
+ * |_|   \___/ \___|_|\_\___|\__|_|  |_|_|_| |_|\___|     |_|  |_|_|
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * @author PocketMine Team
+ * @link http://www.pocketmine.net/
+ *
+ *
+ */
+
+declare(strict_types=1);
+
+namespace pocketmine\block\utils;
+
+use pocketmine\block\Block;
+use pocketmine\entity\projectile\Projectile;
+use pocketmine\math\RayTraceResult;
+use pocketmine\world\sound\AmethystBlockChimeSound;
+use pocketmine\world\sound\BlockPunchSound;
+
+trait AmethystTrait{
+	/**
+	 * @see Block::onProjectileHit()
+	 */
+	public function onProjectileHit(Projectile $projectile, RayTraceResult $hitResult) : void{
+		$this->position->getWorld()->addSound($this->position, new AmethystBlockChimeSound());
+		$this->position->getWorld()->addSound($this->position, new BlockPunchSound($this));
+	}
+}
diff --git a/src/block/utils/AnalogRedstoneSignalEmitterTrait.php b/src/block/utils/AnalogRedstoneSignalEmitterTrait.php
index fe61f652c7a..4b869464360 100644
--- a/src/block/utils/AnalogRedstoneSignalEmitterTrait.php
+++ b/src/block/utils/AnalogRedstoneSignalEmitterTrait.php
@@ -29,7 +29,7 @@ trait AnalogRedstoneSignalEmitterTrait{
 	protected int $signalStrength = 0;
 
 	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
-		$w->boundedInt(4, 0, 15, $this->signalStrength);
+		$w->boundedIntAuto(0, 15, $this->signalStrength);
 	}
 
 	public function getOutputSignalStrength() : int{ return $this->signalStrength; }
diff --git a/src/block/utils/BannerPatternType.php b/src/block/utils/BannerPatternType.php
index 85b06405919..083fada1b3a 100644
--- a/src/block/utils/BannerPatternType.php
+++ b/src/block/utils/BannerPatternType.php
@@ -23,13 +23,11 @@
 
 namespace pocketmine\block\utils;
 
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static BannerPatternType BORDER()
  * @method static BannerPatternType BRICKS()
@@ -70,49 +68,45 @@
  * @method static BannerPatternType TRIANGLE_BOTTOM()
  * @method static BannerPatternType TRIANGLE_TOP()
  */
-final class BannerPatternType{
-	use EnumTrait;
+enum BannerPatternType{
+	use LegacyEnumShimTrait;
 
-	protected static function setup() : void{
-		self::registerAll(
-			new self("border"),
-			new self("bricks"),
-			new self("circle"),
-			new self("creeper"),
-			new self("cross"),
-			new self("curly_border"),
-			new self("diagonal_left"),
-			new self("diagonal_right"),
-			new self("diagonal_up_left"),
-			new self("diagonal_up_right"),
-			new self("flower"),
-			new self("gradient"),
-			new self("gradient_up"),
-			new self("half_horizontal"),
-			new self("half_horizontal_bottom"),
-			new self("half_vertical"),
-			new self("half_vertical_right"),
-			new self("mojang"),
-			new self("rhombus"),
-			new self("skull"),
-			new self("small_stripes"),
-			new self("square_bottom_left"),
-			new self("square_bottom_right"),
-			new self("square_top_left"),
-			new self("square_top_right"),
-			new self("straight_cross"),
-			new self("stripe_bottom"),
-			new self("stripe_center"),
-			new self("stripe_downleft"),
-			new self("stripe_downright"),
-			new self("stripe_left"),
-			new self("stripe_middle"),
-			new self("stripe_right"),
-			new self("stripe_top"),
-			new self("triangle_bottom"),
-			new self("triangle_top"),
-			new self("triangles_bottom"),
-			new self("triangles_top")
-		);
-	}
+	case BORDER;
+	case BRICKS;
+	case CIRCLE;
+	case CREEPER;
+	case CROSS;
+	case CURLY_BORDER;
+	case DIAGONAL_LEFT;
+	case DIAGONAL_RIGHT;
+	case DIAGONAL_UP_LEFT;
+	case DIAGONAL_UP_RIGHT;
+	case FLOWER;
+	case GRADIENT;
+	case GRADIENT_UP;
+	case HALF_HORIZONTAL;
+	case HALF_HORIZONTAL_BOTTOM;
+	case HALF_VERTICAL;
+	case HALF_VERTICAL_RIGHT;
+	case MOJANG;
+	case RHOMBUS;
+	case SKULL;
+	case SMALL_STRIPES;
+	case SQUARE_BOTTOM_LEFT;
+	case SQUARE_BOTTOM_RIGHT;
+	case SQUARE_TOP_LEFT;
+	case SQUARE_TOP_RIGHT;
+	case STRAIGHT_CROSS;
+	case STRIPE_BOTTOM;
+	case STRIPE_CENTER;
+	case STRIPE_DOWNLEFT;
+	case STRIPE_DOWNRIGHT;
+	case STRIPE_LEFT;
+	case STRIPE_MIDDLE;
+	case STRIPE_RIGHT;
+	case STRIPE_TOP;
+	case TRIANGLE_BOTTOM;
+	case TRIANGLE_TOP;
+	case TRIANGLES_BOTTOM;
+	case TRIANGLES_TOP;
 }
diff --git a/src/block/utils/BellAttachmentType.php b/src/block/utils/BellAttachmentType.php
index 963257031bc..29b88a2c400 100644
--- a/src/block/utils/BellAttachmentType.php
+++ b/src/block/utils/BellAttachmentType.php
@@ -23,28 +23,22 @@
 
 namespace pocketmine\block\utils;
 
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static BellAttachmentType CEILING()
  * @method static BellAttachmentType FLOOR()
  * @method static BellAttachmentType ONE_WALL()
  * @method static BellAttachmentType TWO_WALLS()
  */
-final class BellAttachmentType{
-	use EnumTrait;
+enum BellAttachmentType{
+	use LegacyEnumShimTrait;
 
-	protected static function setup() : void{
-		self::registerAll(
-			new self("ceiling"),
-			new self("floor"),
-			new self("one_wall"),
-			new self("two_walls")
-		);
-	}
+	case CEILING;
+	case FLOOR;
+	case ONE_WALL;
+	case TWO_WALLS;
 }
diff --git a/src/block/utils/BrewingStandSlot.php b/src/block/utils/BrewingStandSlot.php
index faeeaa7c4ab..21500b1c977 100644
--- a/src/block/utils/BrewingStandSlot.php
+++ b/src/block/utils/BrewingStandSlot.php
@@ -24,37 +24,31 @@
 namespace pocketmine\block\utils;
 
 use pocketmine\block\inventory\BrewingStandInventory;
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static BrewingStandSlot EAST()
  * @method static BrewingStandSlot NORTHWEST()
  * @method static BrewingStandSlot SOUTHWEST()
  */
-final class BrewingStandSlot{
-	use EnumTrait {
-		__construct as Enum___construct;
-	}
+enum BrewingStandSlot{
+	use LegacyEnumShimTrait;
 
-	protected static function setup() : void{
-		self::registerAll(
-			new self("east", BrewingStandInventory::SLOT_BOTTLE_LEFT),
-			new self("northwest", BrewingStandInventory::SLOT_BOTTLE_MIDDLE),
-			new self("southwest", BrewingStandInventory::SLOT_BOTTLE_RIGHT)
-		);
-	}
-
-	private function __construct(string $enumName, private int $slotNumber){
-		$this->Enum___construct($enumName);
-	}
+	case EAST;
+	case NORTHWEST;
+	case SOUTHWEST;
 
 	/**
 	 * Returns the brewing stand inventory slot number associated with this visual slot.
 	 */
-	public function getSlotNumber() : int{ return $this->slotNumber; }
+	public function getSlotNumber() : int{
+		return match($this){
+			self::EAST => BrewingStandInventory::SLOT_BOTTLE_LEFT,
+			self::NORTHWEST => BrewingStandInventory::SLOT_BOTTLE_MIDDLE,
+			self::SOUTHWEST => BrewingStandInventory::SLOT_BOTTLE_RIGHT
+		};
+	}
 }
diff --git a/src/block/utils/ChiseledBookshelfSlot.php b/src/block/utils/ChiseledBookshelfSlot.php
new file mode 100644
index 00000000000..b5a676bed2b
--- /dev/null
+++ b/src/block/utils/ChiseledBookshelfSlot.php
@@ -0,0 +1,53 @@
+<?php
+
+/*
+ *
+ *  ____            _        _   __  __ _                  __  __ ____
+ * |  _ \ ___   ___| | _____| |_|  \/  (_)_ __   ___      |  \/  |  _ \
+ * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
+ * |  __/ (_) | (__|   <  __/ |_| |  | | | | | |  __/_____| |  | |  __/
+ * |_|   \___/ \___|_|\_\___|\__|_|  |_|_|_| |_|\___|     |_|  |_|_|
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * @author PocketMine Team
+ * @link http://www.pocketmine.net/
+ *
+ *
+ */
+
+declare(strict_types=1);
+
+namespace pocketmine\block\utils;
+
+enum ChiseledBookshelfSlot : int{
+	case TOP_LEFT = 0;
+	case TOP_MIDDLE = 1;
+	case TOP_RIGHT = 2;
+	case BOTTOM_LEFT = 3;
+	case BOTTOM_MIDDLE = 4;
+	case BOTTOM_RIGHT = 5;
+
+	private const SLOTS_PER_SHELF = 3;
+
+	public static function fromBlockFaceCoordinates(float $x, float $y) : self{
+		if($x < 0 || $x > 1){
+			throw new \InvalidArgumentException("X must be between 0 and 1, got $x");
+		}
+		if($y < 0 || $y > 1){
+			throw new \InvalidArgumentException("Y must be between 0 and 1, got $y");
+		}
+
+		$slot = ($y < 0.5 ? self::SLOTS_PER_SHELF : 0) + match(true){
+			//we can't use simple maths here as the action is aligned to the 16x16 pixel grid :(
+			$x < 6 / 16 => 0,
+			$x < 11 / 16 => 1,
+			default => 2
+		};
+
+		return self::from($slot);
+	}
+}
diff --git a/src/block/utils/ColoredTrait.php b/src/block/utils/ColoredTrait.php
index dab86fb6634..2ecd58e2035 100644
--- a/src/block/utils/ColoredTrait.php
+++ b/src/block/utils/ColoredTrait.php
@@ -28,11 +28,11 @@
 
 trait ColoredTrait{
 	/** @var DyeColor */
-	private $color;
+	private $color = DyeColor::WHITE;
 
 	/** @see Block::describeBlockItemState() */
 	public function describeBlockItemState(RuntimeDataDescriber $w) : void{
-		$w->dyeColor($this->color);
+		$w->enum($this->color);
 	}
 
 	public function getColor() : DyeColor{ return $this->color; }
diff --git a/src/block/utils/CopperOxidation.php b/src/block/utils/CopperOxidation.php
index 8278819a790..ba9abeaed31 100644
--- a/src/block/utils/CopperOxidation.php
+++ b/src/block/utils/CopperOxidation.php
@@ -23,57 +23,30 @@
 
 namespace pocketmine\block\utils;
 
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static CopperOxidation EXPOSED()
  * @method static CopperOxidation NONE()
  * @method static CopperOxidation OXIDIZED()
  * @method static CopperOxidation WEATHERED()
  */
-final class CopperOxidation{
-	use EnumTrait {
-		__construct as Enum___construct;
-		register as Enum_register;
-	}
-
-	protected static function setup() : void{
-		self::registerAll(
-			new self("none", 0),
-			new self("exposed", 1),
-			new self("weathered", 2),
-			new self("oxidized", 3)
-		);
-	}
+enum CopperOxidation : int{
+	use LegacyEnumShimTrait;
 
-	protected static function register(self $member) : void{
-		self::Enum_register($member);
-		self::$levelMap[$member->value] = $member;
-	}
-
-	/**
-	 * @var self[]
-	 * @phpstan-var array<int, self>
-	 */
-	private static array $levelMap = [];
-
-	private function __construct(
-		string $name,
-		private int $value
-	){
-		$this->Enum___construct($name);
-	}
+	case NONE = 0;
+	case EXPOSED = 1;
+	case WEATHERED = 2;
+	case OXIDIZED = 3;
 
 	public function getPrevious() : ?self{
-		return self::$levelMap[$this->value - 1] ?? null;
+		return self::tryFrom($this->value - 1);
 	}
 
 	public function getNext() : ?self{
-		return self::$levelMap[$this->value + 1] ?? null;
+		return self::tryFrom($this->value + 1);
 	}
 }
diff --git a/src/block/utils/CopperTrait.php b/src/block/utils/CopperTrait.php
index 5fede94ddd9..5ad8aa82d15 100644
--- a/src/block/utils/CopperTrait.php
+++ b/src/block/utils/CopperTrait.php
@@ -23,8 +23,6 @@
 
 namespace pocketmine\block\utils;
 
-use pocketmine\block\BlockIdentifier;
-use pocketmine\block\BlockTypeInfo;
 use pocketmine\data\runtime\RuntimeDataDescriber;
 use pocketmine\item\Axe;
 use pocketmine\item\Item;
@@ -36,16 +34,11 @@
 use pocketmine\world\sound\ScrapeSound;
 
 trait CopperTrait{
-	private CopperOxidation $oxidation;
+	private CopperOxidation $oxidation = CopperOxidation::NONE;
 	private bool $waxed = false;
 
-	public function __construct(BlockIdentifier $identifier, string $name, BlockTypeInfo $typeInfo){
-		$this->oxidation = CopperOxidation::NONE();
-		parent::__construct($identifier, $name, $typeInfo);
-	}
-
 	public function describeBlockItemState(RuntimeDataDescriber $w) : void{
-		$w->copperOxidation($this->oxidation);
+		$w->enum($this->oxidation);
 		$w->bool($this->waxed);
 	}
 
diff --git a/src/block/utils/CoralType.php b/src/block/utils/CoralType.php
index 5a4d88fa9c5..cba3e8ddee4 100644
--- a/src/block/utils/CoralType.php
+++ b/src/block/utils/CoralType.php
@@ -23,13 +23,11 @@
 
 namespace pocketmine\block\utils;
 
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static CoralType BRAIN()
  * @method static CoralType BUBBLE()
@@ -37,27 +35,22 @@
  * @method static CoralType HORN()
  * @method static CoralType TUBE()
  */
-final class CoralType{
-	use EnumTrait {
-		__construct as Enum___construct;
+enum CoralType{
+	use LegacyEnumShimTrait;
+
+	case TUBE;
+	case BRAIN;
+	case BUBBLE;
+	case FIRE;
+	case HORN;
+
+	public function getDisplayName() : string{
+		return match($this){
+			self::TUBE => "Tube",
+			self::BRAIN => "Brain",
+			self::BUBBLE => "Bubble",
+			self::FIRE => "Fire",
+			self::HORN => "Horn",
+		};
 	}
-
-	protected static function setup() : void{
-		self::registerAll(
-			new self("tube", "Tube"),
-			new self("brain", "Brain"),
-			new self("bubble", "Bubble"),
-			new self("fire", "Fire"),
-			new self("horn", "Horn"),
-		);
-	}
-
-	private function __construct(
-		string $name,
-		private string $displayName
-	){
-		$this->Enum___construct($name);
-	}
-
-	public function getDisplayName() : string{ return $this->displayName; }
 }
diff --git a/src/block/utils/CoralTypeTrait.php b/src/block/utils/CoralTypeTrait.php
index 5dcd539d43a..a335bf9eccb 100644
--- a/src/block/utils/CoralTypeTrait.php
+++ b/src/block/utils/CoralTypeTrait.php
@@ -27,12 +27,12 @@
 use pocketmine\data\runtime\RuntimeDataDescriber;
 
 trait CoralTypeTrait{
-	protected CoralType $coralType;
+	protected CoralType $coralType = CoralType::TUBE;
 	protected bool $dead = false;
 
 	/** @see Block::describeBlockItemState() */
 	public function describeBlockItemState(RuntimeDataDescriber $w) : void{
-		$w->coralType($this->coralType);
+		$w->enum($this->coralType);
 		$w->bool($this->dead);
 	}
 
diff --git a/src/block/utils/DirtType.php b/src/block/utils/DirtType.php
index 536268676d6..9bed30d1c5b 100644
--- a/src/block/utils/DirtType.php
+++ b/src/block/utils/DirtType.php
@@ -23,26 +23,20 @@
 
 namespace pocketmine\block\utils;
 
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static DirtType COARSE()
  * @method static DirtType NORMAL()
  * @method static DirtType ROOTED()
  */
-final class DirtType{
-	use EnumTrait;
+enum DirtType{
+	use LegacyEnumShimTrait;
 
-	protected static function setup() : void{
-		self::registerAll(
-			new self("normal"),
-			new self("coarse"),
-			new self("rooted")
-		);
-	}
+	case NORMAL;
+	case COARSE;
+	case ROOTED;
 }
diff --git a/src/block/utils/DripleafState.php b/src/block/utils/DripleafState.php
index 3c2e20a13eb..4b0cedce7e7 100644
--- a/src/block/utils/DripleafState.php
+++ b/src/block/utils/DripleafState.php
@@ -23,43 +23,30 @@
 
 namespace pocketmine\block\utils;
 
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static DripleafState FULL_TILT()
  * @method static DripleafState PARTIAL_TILT()
  * @method static DripleafState STABLE()
  * @method static DripleafState UNSTABLE()
  */
-final class DripleafState{
-	use EnumTrait {
-		register as Enum_register;
-		__construct as Enum___construct;
-	}
+enum DripleafState{
+	use LegacyEnumShimTrait;
 
-	protected static function setup() : void{
-		self::registerAll(
-			new self("stable", null),
-			new self("unstable", 10),
-			new self("partial_tilt", 10),
-			new self("full_tilt", 100)
-		);
-	}
-
-	private function __construct(
-		string $enumName,
-		private ?int $scheduledUpdateDelayTicks
-	){
-		$this->Enum___construct($enumName);
-	}
+	case STABLE;
+	case UNSTABLE;
+	case PARTIAL_TILT;
+	case FULL_TILT;
 
 	public function getScheduledUpdateDelayTicks() : ?int{
-		return $this->scheduledUpdateDelayTicks;
+		return match($this){
+			self::STABLE => null,
+			self::UNSTABLE, self::PARTIAL_TILT => 10,
+			self::FULL_TILT => 100,
+		};
 	}
-
 }
diff --git a/src/block/utils/DyeColor.php b/src/block/utils/DyeColor.php
index 81c51618a08..5b205eda5e5 100644
--- a/src/block/utils/DyeColor.php
+++ b/src/block/utils/DyeColor.php
@@ -24,13 +24,12 @@
 namespace pocketmine\block\utils;
 
 use pocketmine\color\Color;
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
+use function spl_object_id;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static DyeColor BLACK()
  * @method static DyeColor BLUE()
@@ -48,46 +47,70 @@
  * @method static DyeColor RED()
  * @method static DyeColor WHITE()
  * @method static DyeColor YELLOW()
+ *
+ * @phpstan-type TMetadata array{0: string, 1: Color}
  */
-final class DyeColor{
-	use EnumTrait {
-		__construct as Enum___construct;
-	}
+enum DyeColor{
+	use LegacyEnumShimTrait;
 
-	protected static function setup() : void{
-		self::registerAll(
-			new DyeColor("white", "White", new Color(0xf0, 0xf0, 0xf0)),
-			new DyeColor("orange", "Orange", new Color(0xf9, 0x80, 0x1d)),
-			new DyeColor("magenta", "Magenta", new Color(0xc7, 0x4e, 0xbd)),
-			new DyeColor("light_blue", "Light Blue", new Color(0x3a, 0xb3, 0xda)),
-			new DyeColor("yellow", "Yellow", new Color(0xfe, 0xd8, 0x3d)),
-			new DyeColor("lime", "Lime", new Color(0x80, 0xc7, 0x1f)),
-			new DyeColor("pink", "Pink", new Color(0xf3, 0x8b, 0xaa)),
-			new DyeColor("gray", "Gray", new Color(0x47, 0x4f, 0x52)),
-			new DyeColor("light_gray", "Light Gray", new Color(0x9d, 0x9d, 0x97)),
-			new DyeColor("cyan", "Cyan", new Color(0x16, 0x9c, 0x9c)),
-			new DyeColor("purple", "Purple", new Color(0x89, 0x32, 0xb8)),
-			new DyeColor("blue", "Blue", new Color(0x3c, 0x44, 0xaa)),
-			new DyeColor("brown", "Brown", new Color(0x83, 0x54, 0x32)),
-			new DyeColor("green", "Green", new Color(0x5e, 0x7c, 0x16)),
-			new DyeColor("red", "Red", new Color(0xb0, 0x2e, 0x26)),
-			new DyeColor("black", "Black", new Color(0x1d, 0x1d, 0x21))
-		);
+	case WHITE;
+	case ORANGE;
+	case MAGENTA;
+	case LIGHT_BLUE;
+	case YELLOW;
+	case LIME;
+	case PINK;
+	case GRAY;
+	case LIGHT_GRAY;
+	case CYAN;
+	case PURPLE;
+	case BLUE;
+	case BROWN;
+	case GREEN;
+	case RED;
+	case BLACK;
+
+	/**
+	 * This function exists only to permit the use of named arguments and to make the code easier to read in PhpStorm.
+	 *
+	 * @phpstan-return TMetadata
+	 */
+	private static function meta(string $displayName, Color $rgbValue) : array{
+		return [$displayName, $rgbValue];
 	}
 
-	private function __construct(
-		string $enumName,
-		private string $displayName,
-		private Color $rgbValue
-	){
-		$this->Enum___construct($enumName);
+	/**
+	 * @phpstan-return TMetadata
+	 */
+	private function getMetadata() : array{
+		/** @phpstan-var array<int, TMetadata> $cache */
+		static $cache = [];
+
+		return $cache[spl_object_id($this)] ??= match($this){
+			self::WHITE => self::meta("White", new Color(0xf0, 0xf0, 0xf0)),
+			self::ORANGE => self::meta("Orange", new Color(0xf9, 0x80, 0x1d)),
+			self::MAGENTA => self::meta("Magenta", new Color(0xc7, 0x4e, 0xbd)),
+			self::LIGHT_BLUE => self::meta("Light Blue", new Color(0x3a, 0xb3, 0xda)),
+			self::YELLOW => self::meta("Yellow", new Color(0xfe, 0xd8, 0x3d)),
+			self::LIME => self::meta("Lime", new Color(0x80, 0xc7, 0x1f)),
+			self::PINK => self::meta("Pink", new Color(0xf3, 0x8b, 0xaa)),
+			self::GRAY => self::meta("Gray", new Color(0x47, 0x4f, 0x52)),
+			self::LIGHT_GRAY => self::meta("Light Gray", new Color(0x9d, 0x9d, 0x97)),
+			self::CYAN => self::meta("Cyan", new Color(0x16, 0x9c, 0x9c)),
+			self::PURPLE => self::meta("Purple", new Color(0x89, 0x32, 0xb8)),
+			self::BLUE => self::meta("Blue", new Color(0x3c, 0x44, 0xaa)),
+			self::BROWN => self::meta("Brown", new Color(0x83, 0x54, 0x32)),
+			self::GREEN => self::meta("Green", new Color(0x5e, 0x7c, 0x16)),
+			self::RED => self::meta("Red", new Color(0xb0, 0x2e, 0x26)),
+			self::BLACK => self::meta("Black", new Color(0x1d, 0x1d, 0x21)),
+		};
 	}
 
 	public function getDisplayName() : string{
-		return $this->displayName;
+		return $this->getMetadata()[0];
 	}
 
 	public function getRgbValue() : Color{
-		return $this->rgbValue;
+		return $this->getMetadata()[1];
 	}
 }
diff --git a/src/block/utils/FroglightType.php b/src/block/utils/FroglightType.php
index f6b9c1d13f6..ab20d31ac57 100644
--- a/src/block/utils/FroglightType.php
+++ b/src/block/utils/FroglightType.php
@@ -23,26 +23,20 @@
 
 namespace pocketmine\block\utils;
 
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static FroglightType OCHRE()
  * @method static FroglightType PEARLESCENT()
  * @method static FroglightType VERDANT()
  */
-final class FroglightType{
-	use EnumTrait;
+enum FroglightType{
+	use LegacyEnumShimTrait;
 
-	protected static function setup() : void{
-		self::registerAll(
-			new self("ochre"),
-			new self("pearlescent"),
-			new self("verdant")
-		);
-	}
+	case OCHRE;
+	case PEARLESCENT;
+	case VERDANT;
 }
diff --git a/src/block/utils/LeavesType.php b/src/block/utils/LeavesType.php
index ecb2a1fd949..975551ad6de 100644
--- a/src/block/utils/LeavesType.php
+++ b/src/block/utils/LeavesType.php
@@ -23,13 +23,11 @@
 
 namespace pocketmine\block\utils;
 
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static LeavesType ACACIA()
  * @method static LeavesType AZALEA()
@@ -42,35 +40,32 @@
  * @method static LeavesType OAK()
  * @method static LeavesType SPRUCE()
  */
-final class LeavesType{
-	use EnumTrait {
-		register as Enum_register;
-		__construct as Enum___construct;
-	}
-
-	protected static function setup() : void{
-		self::registerAll(
-			new self("oak", "Oak"),
-			new self("spruce", "Spruce"),
-			new self("birch", "Birch"),
-			new self("jungle", "Jungle"),
-			new self("acacia", "Acacia"),
-			new self("dark_oak", "Dark Oak"),
-			new self("mangrove", "Mangrove"),
-			new self("azalea", "Azalea"),
-			new self("flowering_azalea", "Flowering Azalea"),
-			new self("cherry", "Cherry")
-		);
-	}
+enum LeavesType{
+	use LegacyEnumShimTrait;
 
-	private function __construct(
-		string $enumName,
-		private string $displayName
-	){
-		$this->Enum___construct($enumName);
-	}
+	case OAK;
+	case SPRUCE;
+	case BIRCH;
+	case JUNGLE;
+	case ACACIA;
+	case DARK_OAK;
+	case MANGROVE;
+	case AZALEA;
+	case FLOWERING_AZALEA;
+	case CHERRY;
 
 	public function getDisplayName() : string{
-		return $this->displayName;
+		return match($this){
+			self::OAK => "Oak",
+			self::SPRUCE => "Spruce",
+			self::BIRCH => "Birch",
+			self::JUNGLE => "Jungle",
+			self::ACACIA => "Acacia",
+			self::DARK_OAK => "Dark Oak",
+			self::MANGROVE => "Mangrove",
+			self::AZALEA => "Azalea",
+			self::FLOWERING_AZALEA => "Flowering Azalea",
+			self::CHERRY => "Cherry"
+		};
 	}
 }
diff --git a/src/block/utils/LeverFacing.php b/src/block/utils/LeverFacing.php
index 99eeb88d309..1af92d6c4c5 100644
--- a/src/block/utils/LeverFacing.php
+++ b/src/block/utils/LeverFacing.php
@@ -24,13 +24,11 @@
 namespace pocketmine\block\utils;
 
 use pocketmine\math\Facing;
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static LeverFacing DOWN_AXIS_X()
  * @method static LeverFacing DOWN_AXIS_Z()
@@ -41,27 +39,26 @@
  * @method static LeverFacing UP_AXIS_Z()
  * @method static LeverFacing WEST()
  */
-final class LeverFacing{
-	use EnumTrait {
-		__construct as Enum___construct;
-	}
+enum LeverFacing{
+	use LegacyEnumShimTrait;
 
-	protected static function setup() : void{
-		self::registerAll(
-			new self("up_axis_x", Facing::UP),
-			new self("up_axis_z", Facing::UP),
-			new self("down_axis_x", Facing::DOWN),
-			new self("down_axis_z", Facing::DOWN),
-			new self("north", Facing::NORTH),
-			new self("east", Facing::EAST),
-			new self("south", Facing::SOUTH),
-			new self("west", Facing::WEST),
-		);
-	}
+	case UP_AXIS_X;
+	case UP_AXIS_Z;
+	case DOWN_AXIS_X;
+	case DOWN_AXIS_Z;
+	case NORTH;
+	case EAST;
+	case SOUTH;
+	case WEST;
 
-	private function __construct(string $enumName, private int $facing){
-		$this->Enum___construct($enumName);
+	public function getFacing() : int{
+		return match($this){
+			self::UP_AXIS_X, self::UP_AXIS_Z => Facing::UP,
+			self::DOWN_AXIS_X, self::DOWN_AXIS_Z => Facing::DOWN,
+			self::NORTH => Facing::NORTH,
+			self::EAST => Facing::EAST,
+			self::SOUTH => Facing::SOUTH,
+			self::WEST => Facing::WEST,
+		};
 	}
-
-	public function getFacing() : int{ return $this->facing; }
 }
diff --git a/src/block/utils/MinimumCostFlowCalculator.php b/src/block/utils/MinimumCostFlowCalculator.php
index 84b003bbd12..a690a748709 100644
--- a/src/block/utils/MinimumCostFlowCalculator.php
+++ b/src/block/utils/MinimumCostFlowCalculator.php
@@ -58,17 +58,10 @@ private function calculateFlowCost(int $blockX, int $blockY, int $blockZ, int $a
 			if($j === $originOpposite || $j === $lastOpposite){
 				continue;
 			}
-
-			$x = $blockX;
-			$y = $blockY;
-			$z = $blockZ;
-
-			match($j){
-				Facing::WEST => --$x,
-				Facing::EAST => ++$x,
-				Facing::NORTH => --$z,
-				Facing::SOUTH => ++$z
-			};
+			[$dx, $dy, $dz] = Facing::OFFSET[$j];
+			$x = $blockX + $dx;
+			$y = $blockY + $dy;
+			$z = $blockZ + $dz;
 
 			if(!isset($this->flowCostVisited[$hash = World::blockHash($x, $y, $z)])){
 				if(!$this->world->isInWorld($x, $y, $z) || !$this->canFlowInto($this->world->getBlockAt($x, $y, $z))){
@@ -109,16 +102,10 @@ public function getOptimalFlowDirections(int $originX, int $originY, int $origin
 		$flowCost = array_fill_keys(Facing::HORIZONTAL, 1000);
 		$maxCost = intdiv(4, $this->flowDecayPerBlock);
 		foreach(Facing::HORIZONTAL as $j){
-			$x = $originX;
-			$y = $originY;
-			$z = $originZ;
-
-			match($j){
-				Facing::WEST => --$x,
-				Facing::EAST => ++$x,
-				Facing::NORTH => --$z,
-				Facing::SOUTH => ++$z
-			};
+			[$dx, $dy, $dz] = Facing::OFFSET[$j];
+			$x = $originX + $dx;
+			$y = $originY + $dy;
+			$z = $originZ + $dz;
 
 			if(!$this->world->isInWorld($x, $y, $z) || !$this->canFlowInto($this->world->getBlockAt($x, $y, $z))){
 				$this->flowCostVisited[World::blockHash($x, $y, $z)] = self::BLOCKED;
diff --git a/src/block/utils/MobHeadType.php b/src/block/utils/MobHeadType.php
index 391b6682092..af1fe1c4c2a 100644
--- a/src/block/utils/MobHeadType.php
+++ b/src/block/utils/MobHeadType.php
@@ -23,13 +23,11 @@
 
 namespace pocketmine\block\utils;
 
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static MobHeadType CREEPER()
  * @method static MobHeadType DRAGON()
@@ -39,31 +37,26 @@
  * @method static MobHeadType WITHER_SKELETON()
  * @method static MobHeadType ZOMBIE()
  */
-final class MobHeadType{
-	use EnumTrait {
-		__construct as Enum___construct;
-	}
-
-	protected static function setup() : void{
-		self::registerAll(
-			new MobHeadType("skeleton", "Skeleton Skull"),
-			new MobHeadType("wither_skeleton", "Wither Skeleton Skull"),
-			new MobHeadType("zombie", "Zombie Head"),
-			new MobHeadType("player", "Player Head"),
-			new MobHeadType("creeper", "Creeper Head"),
-			new MobHeadType("dragon", "Dragon Head"),
-			new MobHeadType("piglin", "Piglin Head")
-		);
-	}
+enum MobHeadType{
+	use LegacyEnumShimTrait;
 
-	private function __construct(
-		string $enumName,
-		private string $displayName
-	){
-		$this->Enum___construct($enumName);
-	}
+	case SKELETON;
+	case WITHER_SKELETON;
+	case ZOMBIE;
+	case PLAYER;
+	case CREEPER;
+	case DRAGON;
+	case PIGLIN;
 
 	public function getDisplayName() : string{
-		return $this->displayName;
+		return match($this){
+			self::SKELETON => "Skeleton Skull",
+			self::WITHER_SKELETON => "Wither Skeleton Skull",
+			self::ZOMBIE => "Zombie Head",
+			self::PLAYER => "Player Head",
+			self::CREEPER => "Creeper Head",
+			self::DRAGON => "Dragon Head",
+			self::PIGLIN => "Piglin Head"
+		};
 	}
 }
diff --git a/src/block/utils/MushroomBlockType.php b/src/block/utils/MushroomBlockType.php
index 7f001d13da7..6bc499b2360 100644
--- a/src/block/utils/MushroomBlockType.php
+++ b/src/block/utils/MushroomBlockType.php
@@ -23,13 +23,11 @@
 
 namespace pocketmine\block\utils;
 
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static MushroomBlockType ALL_CAP()
  * @method static MushroomBlockType CAP_EAST()
@@ -43,22 +41,18 @@
  * @method static MushroomBlockType CAP_WEST()
  * @method static MushroomBlockType PORES()
  */
-final class MushroomBlockType{
-	use EnumTrait;
+enum MushroomBlockType{
+	use LegacyEnumShimTrait;
 
-	protected static function setup() : void{
-		self::registerAll(
-			new self("PORES"),
-			new self("CAP_NORTHWEST"),
-			new self("CAP_NORTH"),
-			new self("CAP_NORTHEAST"),
-			new self("CAP_WEST"),
-			new self("CAP_MIDDLE"),
-			new self("CAP_EAST"),
-			new self("CAP_SOUTHWEST"),
-			new self("CAP_SOUTH"),
-			new self("CAP_SOUTHEAST"),
-			new self("ALL_CAP")
-		);
-	}
+	case PORES;
+	case CAP_NORTHWEST;
+	case CAP_NORTH;
+	case CAP_NORTHEAST;
+	case CAP_WEST;
+	case CAP_MIDDLE;
+	case CAP_EAST;
+	case CAP_SOUTHWEST;
+	case CAP_SOUTH;
+	case CAP_SOUTHEAST;
+	case ALL_CAP;
 }
diff --git a/src/block/utils/RecordType.php b/src/block/utils/RecordType.php
index 9b0ab580fc6..e63cee9204d 100644
--- a/src/block/utils/RecordType.php
+++ b/src/block/utils/RecordType.php
@@ -26,13 +26,12 @@
 use pocketmine\lang\KnownTranslationFactory;
 use pocketmine\lang\Translatable;
 use pocketmine\network\mcpe\protocol\types\LevelSoundEvent;
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
+use function spl_object_id;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static RecordType DISK_11()
  * @method static RecordType DISK_13()
@@ -49,48 +48,63 @@
  * @method static RecordType DISK_STRAD()
  * @method static RecordType DISK_WAIT()
  * @method static RecordType DISK_WARD()
+ *
+ * @phpstan-type TMetadata array{0: string, 1: LevelSoundEvent::*, 2: Translatable}
  */
-final class RecordType{
-	use EnumTrait {
-		__construct as Enum___construct;
-	}
+enum RecordType{
+	use LegacyEnumShimTrait;
 
-	protected static function setup() : void{
-		self::registerAll(
-			new RecordType("disk_13", "C418 - 13", LevelSoundEvent::RECORD_13, KnownTranslationFactory::item_record_13_desc()),
-			new RecordType("disk_5", "Samuel Åberg - 5", LevelSoundEvent::RECORD_5, KnownTranslationFactory::item_record_5_desc()),
-			new RecordType("disk_cat", "C418 - cat", LevelSoundEvent::RECORD_CAT, KnownTranslationFactory::item_record_cat_desc()),
-			new RecordType("disk_blocks", "C418 - blocks", LevelSoundEvent::RECORD_BLOCKS, KnownTranslationFactory::item_record_blocks_desc()),
-			new RecordType("disk_chirp", "C418 - chirp", LevelSoundEvent::RECORD_CHIRP, KnownTranslationFactory::item_record_chirp_desc()),
-			new RecordType("disk_far", "C418 - far", LevelSoundEvent::RECORD_FAR, KnownTranslationFactory::item_record_far_desc()),
-			new RecordType("disk_mall", "C418 - mall", LevelSoundEvent::RECORD_MALL, KnownTranslationFactory::item_record_mall_desc()),
-			new RecordType("disk_mellohi", "C418 - mellohi", LevelSoundEvent::RECORD_MELLOHI, KnownTranslationFactory::item_record_mellohi_desc()),
-			new RecordType("disk_otherside", "Lena Raine - otherside", LevelSoundEvent::RECORD_OTHERSIDE, KnownTranslationFactory::item_record_otherside_desc()),
-			new RecordType("disk_pigstep", "Lena Raine - Pigstep", LevelSoundEvent::RECORD_PIGSTEP, KnownTranslationFactory::item_record_pigstep_desc()),
-			new RecordType("disk_stal", "C418 - stal", LevelSoundEvent::RECORD_STAL, KnownTranslationFactory::item_record_stal_desc()),
-			new RecordType("disk_strad", "C418 - strad", LevelSoundEvent::RECORD_STRAD, KnownTranslationFactory::item_record_strad_desc()),
-			new RecordType("disk_ward", "C418 - ward", LevelSoundEvent::RECORD_WARD, KnownTranslationFactory::item_record_ward_desc()),
-			new RecordType("disk_11", "C418 - 11", LevelSoundEvent::RECORD_11, KnownTranslationFactory::item_record_11_desc()),
-			new RecordType("disk_wait", "C418 - wait", LevelSoundEvent::RECORD_WAIT, KnownTranslationFactory::item_record_wait_desc())
-		);
-	}
+	case DISK_13;
+	case DISK_5;
+	case DISK_CAT;
+	case DISK_BLOCKS;
+	case DISK_CHIRP;
+	case DISK_FAR;
+	case DISK_MALL;
+	case DISK_MELLOHI;
+	case DISK_OTHERSIDE;
+	case DISK_PIGSTEP;
+	case DISK_STAL;
+	case DISK_STRAD;
+	case DISK_WARD;
+	case DISK_11;
+	case DISK_WAIT;
 
-	private function __construct(
-		string $enumName,
-		private string $soundName,
-		private int $soundId,
-		private Translatable $translatableName
-	){
-		$this->Enum___construct($enumName);
+	/**
+	 * @phpstan-return TMetadata
+	 */
+	private function getMetadata() : array{
+		/** @phpstan-var array<int, TMetadata> $cache */
+		static $cache = [];
+
+		return $cache[spl_object_id($this)] ??= match($this){
+			self::DISK_13 => ["C418 - 13", LevelSoundEvent::RECORD_13, KnownTranslationFactory::item_record_13_desc()],
+			self::DISK_5 => ["Samuel Åberg - 5", LevelSoundEvent::RECORD_5, KnownTranslationFactory::item_record_5_desc()],
+			self::DISK_CAT => ["C418 - cat", LevelSoundEvent::RECORD_CAT, KnownTranslationFactory::item_record_cat_desc()],
+			self::DISK_BLOCKS => ["C418 - blocks", LevelSoundEvent::RECORD_BLOCKS, KnownTranslationFactory::item_record_blocks_desc()],
+			self::DISK_CHIRP => ["C418 - chirp", LevelSoundEvent::RECORD_CHIRP, KnownTranslationFactory::item_record_chirp_desc()],
+			self::DISK_FAR => ["C418 - far", LevelSoundEvent::RECORD_FAR, KnownTranslationFactory::item_record_far_desc()],
+			self::DISK_MALL => ["C418 - mall", LevelSoundEvent::RECORD_MALL, KnownTranslationFactory::item_record_mall_desc()],
+			self::DISK_MELLOHI => ["C418 - mellohi", LevelSoundEvent::RECORD_MELLOHI, KnownTranslationFactory::item_record_mellohi_desc()],
+			self::DISK_OTHERSIDE => ["Lena Raine - otherside", LevelSoundEvent::RECORD_OTHERSIDE, KnownTranslationFactory::item_record_otherside_desc()],
+			self::DISK_PIGSTEP => ["Lena Raine - Pigstep", LevelSoundEvent::RECORD_PIGSTEP, KnownTranslationFactory::item_record_pigstep_desc()],
+			self::DISK_STAL => ["C418 - stal", LevelSoundEvent::RECORD_STAL, KnownTranslationFactory::item_record_stal_desc()],
+			self::DISK_STRAD => ["C418 - strad", LevelSoundEvent::RECORD_STRAD, KnownTranslationFactory::item_record_strad_desc()],
+			self::DISK_WARD => ["C418 - ward", LevelSoundEvent::RECORD_WARD, KnownTranslationFactory::item_record_ward_desc()],
+			self::DISK_11 => ["C418 - 11", LevelSoundEvent::RECORD_11, KnownTranslationFactory::item_record_11_desc()],
+			self::DISK_WAIT => ["C418 - wait", LevelSoundEvent::RECORD_WAIT, KnownTranslationFactory::item_record_wait_desc()]
+		};
 	}
 
 	public function getSoundName() : string{
-		return $this->soundName;
+		return $this->getMetadata()[0];
 	}
 
 	public function getSoundId() : int{
-		return $this->soundId;
+		return $this->getMetadata()[1];
 	}
 
-	public function getTranslatableName() : Translatable{ return $this->translatableName; }
+	public function getTranslatableName() : Translatable{
+		return $this->getMetadata()[2];
+	}
 }
diff --git a/src/block/utils/SaplingType.php b/src/block/utils/SaplingType.php
index 516ee1516bc..9fe87936cd0 100644
--- a/src/block/utils/SaplingType.php
+++ b/src/block/utils/SaplingType.php
@@ -23,14 +23,12 @@
 
 namespace pocketmine\block\utils;
 
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 use pocketmine\world\generator\object\TreeType;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static SaplingType ACACIA()
  * @method static SaplingType BIRCH()
@@ -39,33 +37,29 @@
  * @method static SaplingType OAK()
  * @method static SaplingType SPRUCE()
  */
-final class SaplingType{
-	use EnumTrait {
-		__construct as Enum___construct;
-	}
+enum SaplingType{
+	use LegacyEnumShimTrait;
 
-	protected static function setup() : void{
-		self::registerAll(
-			new self("oak", TreeType::OAK()),
-			new self("spruce", TreeType::SPRUCE()),
-			new self("birch", TreeType::BIRCH()),
-			new self("jungle", TreeType::JUNGLE()),
-			new self("acacia", TreeType::ACACIA()),
-			new self("dark_oak", TreeType::DARK_OAK()),
-			//TODO: cherry
-		);
-	}
+	case OAK;
+	case SPRUCE;
+	case BIRCH;
+	case JUNGLE;
+	case ACACIA;
+	case DARK_OAK;
+	//TODO: cherry
 
-	private function __construct(
-		string $enumName,
-		private TreeType $treeType,
-	){
-		$this->Enum___construct($enumName);
+	public function getTreeType() : TreeType{
+		return match($this){
+			self::OAK => TreeType::OAK,
+			self::SPRUCE => TreeType::SPRUCE,
+			self::BIRCH => TreeType::BIRCH,
+			self::JUNGLE => TreeType::JUNGLE,
+			self::ACACIA => TreeType::ACACIA,
+			self::DARK_OAK => TreeType::DARK_OAK,
+		};
 	}
 
-	public function getTreeType() : TreeType{ return $this->treeType; }
-
 	public function getDisplayName() : string{
-		return $this->treeType->getDisplayName();
+		return $this->getTreeType()->getDisplayName();
 	}
 }
diff --git a/src/block/utils/SignLikeRotationTrait.php b/src/block/utils/SignLikeRotationTrait.php
index 233c750954d..36b2dce3fa1 100644
--- a/src/block/utils/SignLikeRotationTrait.php
+++ b/src/block/utils/SignLikeRotationTrait.php
@@ -31,7 +31,7 @@ trait SignLikeRotationTrait{
 	private $rotation = 0;
 
 	protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
-		$w->boundedInt(4, 0, 15, $this->rotation);
+		$w->boundedIntAuto(0, 15, $this->rotation);
 	}
 
 	public function getRotation() : int{ return $this->rotation; }
diff --git a/src/block/utils/SlabType.php b/src/block/utils/SlabType.php
index 1da1b3e5a34..90f3e095357 100644
--- a/src/block/utils/SlabType.php
+++ b/src/block/utils/SlabType.php
@@ -23,26 +23,20 @@
 
 namespace pocketmine\block\utils;
 
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static SlabType BOTTOM()
  * @method static SlabType DOUBLE()
  * @method static SlabType TOP()
  */
-final class SlabType{
-	use EnumTrait;
+enum SlabType{
+	use LegacyEnumShimTrait;
 
-	protected static function setup() : void{
-		self::registerAll(
-			new self("bottom"),
-			new self("top"),
-			new self("double")
-		);
-	}
+	case BOTTOM;
+	case TOP;
+	case DOUBLE;
 }
diff --git a/src/block/utils/StairShape.php b/src/block/utils/StairShape.php
index c98aec8fbad..b89b9adbb29 100644
--- a/src/block/utils/StairShape.php
+++ b/src/block/utils/StairShape.php
@@ -23,13 +23,11 @@
 
 namespace pocketmine\block\utils;
 
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static StairShape INNER_LEFT()
  * @method static StairShape INNER_RIGHT()
@@ -37,16 +35,12 @@
  * @method static StairShape OUTER_RIGHT()
  * @method static StairShape STRAIGHT()
  */
-final class StairShape{
-	use EnumTrait;
+enum StairShape{
+	use LegacyEnumShimTrait;
 
-	protected static function setup() : void{
-		self::registerAll(
-			new self("straight"),
-			new self("inner_left"),
-			new self("inner_right"),
-			new self("outer_left"),
-			new self("outer_right")
-		);
-	}
+	case STRAIGHT;
+	case INNER_LEFT;
+	case INNER_RIGHT;
+	case OUTER_LEFT;
+	case OUTER_RIGHT;
 }
diff --git a/src/block/utils/StaticSupportTrait.php b/src/block/utils/StaticSupportTrait.php
new file mode 100644
index 00000000000..0e40230cc6e
--- /dev/null
+++ b/src/block/utils/StaticSupportTrait.php
@@ -0,0 +1,57 @@
+<?php
+
+/*
+ *
+ *  ____            _        _   __  __ _                  __  __ ____
+ * |  _ \ ___   ___| | _____| |_|  \/  (_)_ __   ___      |  \/  |  _ \
+ * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
+ * |  __/ (_) | (__|   <  __/ |_| |  | | | | | |  __/_____| |  | |  __/
+ * |_|   \___/ \___|_|\_\___|\__|_|  |_|_|_| |_|\___|     |_|  |_|_|
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * @author PocketMine Team
+ * @link http://www.pocketmine.net/
+ *
+ *
+ */
+
+declare(strict_types=1);
+
+namespace pocketmine\block\utils;
+
+use pocketmine\block\Block;
+use pocketmine\math\Vector3;
+
+/**
+ * Used by blocks which always have the same support requirements no matter what state they are in.
+ * Prevents placement if support isn't available, and automatically destroys itself if support is removed.
+ */
+trait StaticSupportTrait{
+
+	/**
+	 * Implement this to define the block's support requirements.
+	 */
+	abstract private function canBeSupportedAt(Block $block) : bool;
+
+	/**
+	 * @see Block::canBePlacedAt()
+	 */
+	public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
+		return $this->canBeSupportedAt($blockReplace) && parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
+	}
+
+	/**
+	 * @see Block::onNearbyBlockChange()
+	 */
+	public function onNearbyBlockChange() : void{
+		if(!$this->canBeSupportedAt($this)){
+			$this->position->getWorld()->useBreakOn($this->position);
+		}else{
+			parent::onNearbyBlockChange();
+		}
+	}
+}
diff --git a/src/block/utils/SupportType.php b/src/block/utils/SupportType.php
index b4480bf57fd..9513d864011 100644
--- a/src/block/utils/SupportType.php
+++ b/src/block/utils/SupportType.php
@@ -23,36 +23,30 @@
 
 namespace pocketmine\block\utils;
 
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static SupportType CENTER()
  * @method static SupportType EDGE()
  * @method static SupportType FULL()
  * @method static SupportType NONE()
  */
-final class SupportType{
-	use EnumTrait;
-
-	protected static function setup() : void{
-		self::registerAll(
-			new self("full"),
-			new self("center"),
-			new self("edge"),
-			new self("none")
-		);
-	}
+enum SupportType{
+	use LegacyEnumShimTrait;
+
+	case FULL;
+	case CENTER;
+	case EDGE;
+	case NONE;
 
 	public function hasEdgeSupport() : bool{
-		return $this->equals(self::EDGE()) || $this->equals(self::FULL());
+		return $this === self::EDGE || $this === self::FULL;
 	}
 
 	public function hasCenterSupport() : bool{
-		return $this->equals(self::CENTER()) || $this->equals(self::FULL());
+		return $this === self::CENTER || $this === self::FULL;
 	}
 }
diff --git a/src/block/utils/WallConnectionType.php b/src/block/utils/WallConnectionType.php
index 11765ab33c9..1d4c8d15243 100644
--- a/src/block/utils/WallConnectionType.php
+++ b/src/block/utils/WallConnectionType.php
@@ -23,24 +23,18 @@
 
 namespace pocketmine\block\utils;
 
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static WallConnectionType SHORT()
  * @method static WallConnectionType TALL()
  */
-final class WallConnectionType{
-	use EnumTrait;
+enum WallConnectionType{
+	use LegacyEnumShimTrait;
 
-	protected static function setup() : void{
-		self::registerAll(
-			new self("short"),
-			new self("tall")
-		);
-	}
+	case SHORT;
+	case TALL;
 }
diff --git a/src/block/utils/WoodType.php b/src/block/utils/WoodType.php
index b9d96561b98..f6195b9f906 100644
--- a/src/block/utils/WoodType.php
+++ b/src/block/utils/WoodType.php
@@ -23,13 +23,11 @@
 
 namespace pocketmine\block\utils;
 
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static WoodType ACACIA()
  * @method static WoodType BIRCH()
@@ -42,41 +40,44 @@
  * @method static WoodType SPRUCE()
  * @method static WoodType WARPED()
  */
-final class WoodType{
-	use EnumTrait {
-		__construct as private Enum___construct;
-	}
+enum WoodType{
+	use LegacyEnumShimTrait;
 
-	protected static function setup() : void{
-		self::registerAll(
-			new self("oak", "Oak", true),
-			new self("spruce", "Spruce", true),
-			new self("birch", "Birch", true),
-			new self("jungle", "Jungle", true),
-			new self("acacia", "Acacia", true),
-			new self("dark_oak", "Dark Oak", true),
-			new self("mangrove", "Mangrove", true),
-			new self("crimson", "Crimson", false, "Stem", "Hyphae"),
-			new self("warped", "Warped", false, "Stem", "Hyphae"),
-			new self("cherry", "Cherry", true),
-		);
-	}
+	case OAK;
+	case SPRUCE;
+	case BIRCH;
+	case JUNGLE;
+	case ACACIA;
+	case DARK_OAK;
+	case MANGROVE;
+	case CRIMSON;
+	case WARPED;
+	case CHERRY;
 
-	private function __construct(
-		string $enumName,
-		private string $displayName,
-		private bool $flammable,
-		private ?string $standardLogSuffix = null,
-		private ?string $allSidedLogSuffix = null,
-	){
-		$this->Enum___construct($enumName);
+	public function getDisplayName() : string{
+		return match($this){
+			self::OAK => "Oak",
+			self::SPRUCE => "Spruce",
+			self::BIRCH => "Birch",
+			self::JUNGLE => "Jungle",
+			self::ACACIA => "Acacia",
+			self::DARK_OAK => "Dark Oak",
+			self::MANGROVE => "Mangrove",
+			self::CRIMSON => "Crimson",
+			self::WARPED => "Warped",
+			self::CHERRY => "Cherry",
+		};
 	}
 
-	public function getDisplayName() : string{ return $this->displayName; }
-
-	public function isFlammable() : bool{ return $this->flammable; }
+	public function isFlammable() : bool{
+		return $this !== self::CRIMSON && $this !== self::WARPED;
+	}
 
-	public function getStandardLogSuffix() : ?string{ return $this->standardLogSuffix; }
+	public function getStandardLogSuffix() : ?string{
+		return $this === self::CRIMSON || $this === self::WARPED ? "Stem" : null;
+	}
 
-	public function getAllSidedLogSuffix() : ?string{ return $this->allSidedLogSuffix; }
+	public function getAllSidedLogSuffix() : ?string{
+		return $this === self::CRIMSON || $this === self::WARPED ? "Hyphae" : null;
+	}
 }
diff --git a/src/command/defaults/DefaultGamemodeCommand.php b/src/command/defaults/DefaultGamemodeCommand.php
index bac98e6783b..d3030eb27b3 100644
--- a/src/command/defaults/DefaultGamemodeCommand.php
+++ b/src/command/defaults/DefaultGamemodeCommand.php
@@ -53,7 +53,8 @@ public function execute(CommandSender $sender, string $commandLabel, array $args
 			return true;
 		}
 
-		$sender->getServer()->getConfigGroup()->setConfigString(ServerProperties::GAME_MODE, $gameMode->name());
+		//TODO: this probably shouldn't use the enum name directly
+		$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/GamemodeCommand.php b/src/command/defaults/GamemodeCommand.php
index 86ca8e9df66..666626a692d 100644
--- a/src/command/defaults/GamemodeCommand.php
+++ b/src/command/defaults/GamemodeCommand.php
@@ -61,13 +61,13 @@ public function execute(CommandSender $sender, string $commandLabel, array $args
 			return true;
 		}
 
-		if($target->getGamemode()->equals($gameMode)){
+		if($target->getGamemode() === $gameMode){
 			$sender->sendMessage(KnownTranslationFactory::pocketmine_command_gamemode_failure($target->getName()));
 			return true;
 		}
 
 		$target->setGamemode($gameMode);
-		if(!$gameMode->equals($target->getGamemode())){
+		if($gameMode !== $target->getGamemode()){
 			$sender->sendMessage(KnownTranslationFactory::pocketmine_command_gamemode_failure($target->getName()));
 		}else{
 			if($target === $sender){
diff --git a/src/crafting/CraftingManager.php b/src/crafting/CraftingManager.php
index 74bc2ba1022..c7c0b10c645 100644
--- a/src/crafting/CraftingManager.php
+++ b/src/crafting/CraftingManager.php
@@ -29,6 +29,7 @@
 use pocketmine\utils\BinaryStream;
 use pocketmine\utils\DestructorCallbackTrait;
 use pocketmine\utils\ObjectSet;
+use function spl_object_id;
 use function usort;
 
 class CraftingManager{
@@ -80,8 +81,8 @@ class CraftingManager{
 
 	public function __construct(){
 		$this->recipeRegisteredCallbacks = new ObjectSet();
-		foreach(FurnaceType::getAll() as $furnaceType){
-			$this->furnaceRecipeManagers[$furnaceType->id()] = new FurnaceRecipeManager();
+		foreach(FurnaceType::cases() as $furnaceType){
+			$this->furnaceRecipeManagers[spl_object_id($furnaceType)] = new FurnaceRecipeManager();
 		}
 
 		$recipeRegisteredCallbacks = $this->recipeRegisteredCallbacks;
@@ -177,7 +178,7 @@ public function getCraftingRecipeFromIndex(int $index) : ?CraftingRecipe{
 	}
 
 	public function getFurnaceRecipeManager(FurnaceType $furnaceType) : FurnaceRecipeManager{
-		return $this->furnaceRecipeManagers[$furnaceType->id()];
+		return $this->furnaceRecipeManagers[spl_object_id($furnaceType)];
 	}
 
 	/**
diff --git a/src/crafting/CraftingManagerFromDataHelper.php b/src/crafting/CraftingManagerFromDataHelper.php
index 812ff83e0af..8315f2b3b33 100644
--- a/src/crafting/CraftingManagerFromDataHelper.php
+++ b/src/crafting/CraftingManagerFromDataHelper.php
@@ -211,10 +211,10 @@ public static function make(string $directoryPath) : CraftingManager{
 
 		foreach(self::loadJsonArrayOfObjectsFile(Path::join($directoryPath, 'shapeless_crafting.json'), ShapelessRecipeData::class) as $recipe){
 			$recipeType = match($recipe->block){
-				"crafting_table" => ShapelessRecipeType::CRAFTING(),
-				"stonecutter" => ShapelessRecipeType::STONECUTTER(),
-				"smithing_table" => ShapelessRecipeType::SMITHING(),
-				"cartography_table" => ShapelessRecipeType::CARTOGRAPHY(),
+				"crafting_table" => ShapelessRecipeType::CRAFTING,
+				"stonecutter" => ShapelessRecipeType::STONECUTTER,
+				"smithing_table" => ShapelessRecipeType::SMITHING,
+				"cartography_table" => ShapelessRecipeType::CARTOGRAPHY,
 				default => null
 			};
 			if($recipeType === null){
@@ -270,9 +270,9 @@ public static function make(string $directoryPath) : CraftingManager{
 		}
 		foreach(self::loadJsonArrayOfObjectsFile(Path::join($directoryPath, 'smelting.json'), FurnaceRecipeData::class) as $recipe){
 			$furnaceType = match ($recipe->block){
-				"furnace" => FurnaceType::FURNACE(),
-				"blast_furnace" => FurnaceType::BLAST_FURNACE(),
-				"smoker" => FurnaceType::SMOKER(),
+				"furnace" => FurnaceType::FURNACE,
+				"blast_furnace" => FurnaceType::BLAST_FURNACE,
+				"smoker" => FurnaceType::SMOKER,
 				//TODO: campfire
 				default => null
 			};
diff --git a/src/crafting/FurnaceType.php b/src/crafting/FurnaceType.php
index 649ff4484df..0ce5b72cea6 100644
--- a/src/crafting/FurnaceType.php
+++ b/src/crafting/FurnaceType.php
@@ -23,40 +23,45 @@
 
 namespace pocketmine\crafting;
 
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 use pocketmine\world\sound\BlastFurnaceSound;
 use pocketmine\world\sound\FurnaceSound;
 use pocketmine\world\sound\SmokerSound;
 use pocketmine\world\sound\Sound;
+use function spl_object_id;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static FurnaceType BLAST_FURNACE()
  * @method static FurnaceType FURNACE()
  * @method static FurnaceType SMOKER()
+ *
+ * @phpstan-type TMetadata array{0: int, 1: Sound}
  */
-final class FurnaceType{
-	use EnumTrait {
-		__construct as Enum___construct;
-	}
+enum FurnaceType{
+	use LegacyEnumShimTrait;
 
-	protected static function setup() : void{
-		self::registerAll(
-			new self("furnace", 200, new FurnaceSound()),
-			new self("blast_furnace", 100, new BlastFurnaceSound()),
-			new self("smoker", 100, new SmokerSound()),
-		);
-	}
+	case FURNACE;
+	case BLAST_FURNACE;
+	case SMOKER;
+
+	/**
+	 * @phpstan-return TMetadata
+	 */
+	private function getMetadata() : array{
+		/** @phpstan-var array<int, TMetadata> $cache */
+		static $cache = [];
 
-	private function __construct(string $enumName, private int $cookDurationTicks, private Sound $cookSound){
-		$this->Enum___construct($enumName);
+		return $cache[spl_object_id($this)] ??= match($this){
+			self::FURNACE => [200, new FurnaceSound()],
+			self::BLAST_FURNACE => [100, new BlastFurnaceSound()],
+			self::SMOKER => [100, new SmokerSound()],
+		};
 	}
 
-	public function getCookDurationTicks() : int{ return $this->cookDurationTicks; }
+	public function getCookDurationTicks() : int{ return $this->getMetadata()[0]; }
 
-	public function getCookSound() : Sound{ return $this->cookSound; }
+	public function getCookSound() : Sound{ return $this->getMetadata()[1]; }
 }
diff --git a/src/crafting/ShapelessRecipeType.php b/src/crafting/ShapelessRecipeType.php
index b115c22f49c..4f4624a4bff 100644
--- a/src/crafting/ShapelessRecipeType.php
+++ b/src/crafting/ShapelessRecipeType.php
@@ -23,28 +23,22 @@
 
 namespace pocketmine\crafting;
 
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static ShapelessRecipeType CARTOGRAPHY()
  * @method static ShapelessRecipeType CRAFTING()
  * @method static ShapelessRecipeType SMITHING()
  * @method static ShapelessRecipeType STONECUTTER()
  */
-final class ShapelessRecipeType{
-	use EnumTrait;
+enum ShapelessRecipeType{
+	use LegacyEnumShimTrait;
 
-	protected static function setup() : void{
-		self::registerAll(
-			new self("crafting"),
-			new self("stonecutter"),
-			new self("smithing"),
-			new self("cartography")
-		);
-	}
+	case CRAFTING;
+	case STONECUTTER;
+	case SMITHING;
+	case CARTOGRAPHY;
 }
diff --git a/src/crash/CrashDump.php b/src/crash/CrashDump.php
index b0e58fd166c..1a558b499dd 100644
--- a/src/crash/CrashDump.php
+++ b/src/crash/CrashDump.php
@@ -143,7 +143,7 @@ private function pluginsData() : void{
 					depends: $d->getDepend(),
 					softDepends: $d->getSoftDepend(),
 					main: $d->getMain(),
-					load: mb_strtoupper($d->getOrder()->name()),
+					load: mb_strtoupper($d->getOrder()->name),
 					website: $d->getWebsite()
 				);
 			}
diff --git a/src/data/bedrock/BannerPatternTypeIdMap.php b/src/data/bedrock/BannerPatternTypeIdMap.php
index 293f8ec3870..064844e199f 100644
--- a/src/data/bedrock/BannerPatternTypeIdMap.php
+++ b/src/data/bedrock/BannerPatternTypeIdMap.php
@@ -26,6 +26,7 @@
 use pocketmine\block\utils\BannerPatternType;
 use pocketmine\utils\SingletonTrait;
 use function array_key_exists;
+use function spl_object_id;
 
 final class BannerPatternTypeIdMap{
 	use SingletonTrait;
@@ -42,49 +43,49 @@ final class BannerPatternTypeIdMap{
 	private array $enumToId = [];
 
 	public function __construct(){
-		$this->register("bo", BannerPatternType::BORDER());
-		$this->register("bri", BannerPatternType::BRICKS());
-		$this->register("mc", BannerPatternType::CIRCLE());
-		$this->register("cre", BannerPatternType::CREEPER());
-		$this->register("cr", BannerPatternType::CROSS());
-		$this->register("cbo", BannerPatternType::CURLY_BORDER());
-		$this->register("lud", BannerPatternType::DIAGONAL_LEFT());
-		$this->register("rd", BannerPatternType::DIAGONAL_RIGHT());
-		$this->register("ld", BannerPatternType::DIAGONAL_UP_LEFT());
-		$this->register("rud", BannerPatternType::DIAGONAL_UP_RIGHT());
-		$this->register("flo", BannerPatternType::FLOWER());
-		$this->register("gra", BannerPatternType::GRADIENT());
-		$this->register("gru", BannerPatternType::GRADIENT_UP());
-		$this->register("hh", BannerPatternType::HALF_HORIZONTAL());
-		$this->register("hhb", BannerPatternType::HALF_HORIZONTAL_BOTTOM());
-		$this->register("vh", BannerPatternType::HALF_VERTICAL());
-		$this->register("vhr", BannerPatternType::HALF_VERTICAL_RIGHT());
-		$this->register("moj", BannerPatternType::MOJANG());
-		$this->register("mr", BannerPatternType::RHOMBUS());
-		$this->register("sku", BannerPatternType::SKULL());
-		$this->register("ss", BannerPatternType::SMALL_STRIPES());
-		$this->register("bl", BannerPatternType::SQUARE_BOTTOM_LEFT());
-		$this->register("br", BannerPatternType::SQUARE_BOTTOM_RIGHT());
-		$this->register("tl", BannerPatternType::SQUARE_TOP_LEFT());
-		$this->register("tr", BannerPatternType::SQUARE_TOP_RIGHT());
-		$this->register("sc", BannerPatternType::STRAIGHT_CROSS());
-		$this->register("bs", BannerPatternType::STRIPE_BOTTOM());
-		$this->register("cs", BannerPatternType::STRIPE_CENTER());
-		$this->register("dls", BannerPatternType::STRIPE_DOWNLEFT());
-		$this->register("drs", BannerPatternType::STRIPE_DOWNRIGHT());
-		$this->register("ls", BannerPatternType::STRIPE_LEFT());
-		$this->register("ms", BannerPatternType::STRIPE_MIDDLE());
-		$this->register("rs", BannerPatternType::STRIPE_RIGHT());
-		$this->register("ts", BannerPatternType::STRIPE_TOP());
-		$this->register("bt", BannerPatternType::TRIANGLE_BOTTOM());
-		$this->register("tt", BannerPatternType::TRIANGLE_TOP());
-		$this->register("bts", BannerPatternType::TRIANGLES_BOTTOM());
-		$this->register("tts", BannerPatternType::TRIANGLES_TOP());
+		$this->register("bo", BannerPatternType::BORDER);
+		$this->register("bri", BannerPatternType::BRICKS);
+		$this->register("mc", BannerPatternType::CIRCLE);
+		$this->register("cre", BannerPatternType::CREEPER);
+		$this->register("cr", BannerPatternType::CROSS);
+		$this->register("cbo", BannerPatternType::CURLY_BORDER);
+		$this->register("lud", BannerPatternType::DIAGONAL_LEFT);
+		$this->register("rd", BannerPatternType::DIAGONAL_RIGHT);
+		$this->register("ld", BannerPatternType::DIAGONAL_UP_LEFT);
+		$this->register("rud", BannerPatternType::DIAGONAL_UP_RIGHT);
+		$this->register("flo", BannerPatternType::FLOWER);
+		$this->register("gra", BannerPatternType::GRADIENT);
+		$this->register("gru", BannerPatternType::GRADIENT_UP);
+		$this->register("hh", BannerPatternType::HALF_HORIZONTAL);
+		$this->register("hhb", BannerPatternType::HALF_HORIZONTAL_BOTTOM);
+		$this->register("vh", BannerPatternType::HALF_VERTICAL);
+		$this->register("vhr", BannerPatternType::HALF_VERTICAL_RIGHT);
+		$this->register("moj", BannerPatternType::MOJANG);
+		$this->register("mr", BannerPatternType::RHOMBUS);
+		$this->register("sku", BannerPatternType::SKULL);
+		$this->register("ss", BannerPatternType::SMALL_STRIPES);
+		$this->register("bl", BannerPatternType::SQUARE_BOTTOM_LEFT);
+		$this->register("br", BannerPatternType::SQUARE_BOTTOM_RIGHT);
+		$this->register("tl", BannerPatternType::SQUARE_TOP_LEFT);
+		$this->register("tr", BannerPatternType::SQUARE_TOP_RIGHT);
+		$this->register("sc", BannerPatternType::STRAIGHT_CROSS);
+		$this->register("bs", BannerPatternType::STRIPE_BOTTOM);
+		$this->register("cs", BannerPatternType::STRIPE_CENTER);
+		$this->register("dls", BannerPatternType::STRIPE_DOWNLEFT);
+		$this->register("drs", BannerPatternType::STRIPE_DOWNRIGHT);
+		$this->register("ls", BannerPatternType::STRIPE_LEFT);
+		$this->register("ms", BannerPatternType::STRIPE_MIDDLE);
+		$this->register("rs", BannerPatternType::STRIPE_RIGHT);
+		$this->register("ts", BannerPatternType::STRIPE_TOP);
+		$this->register("bt", BannerPatternType::TRIANGLE_BOTTOM);
+		$this->register("tt", BannerPatternType::TRIANGLE_TOP);
+		$this->register("bts", BannerPatternType::TRIANGLES_BOTTOM);
+		$this->register("tts", BannerPatternType::TRIANGLES_TOP);
 	}
 
 	public function register(string $stringId, BannerPatternType $type) : void{
 		$this->idToEnum[$stringId] = $type;
-		$this->enumToId[$type->id()] = $stringId;
+		$this->enumToId[spl_object_id($type)] = $stringId;
 	}
 
 	public function fromId(string $id) : ?BannerPatternType{
@@ -92,9 +93,10 @@ public function fromId(string $id) : ?BannerPatternType{
 	}
 
 	public function toId(BannerPatternType $type) : string{
-		if(!array_key_exists($type->id(), $this->enumToId)){
-			throw new \InvalidArgumentException("Missing mapping for banner pattern type " . $type->name());
+		$k = spl_object_id($type);
+		if(!array_key_exists($k, $this->enumToId)){
+			throw new \InvalidArgumentException("Missing mapping for banner pattern type " . $type->name);
 		}
-		return $this->enumToId[$type->id()];
+		return $this->enumToId[$k];
 	}
 }
diff --git a/src/data/bedrock/DyeColorIdMap.php b/src/data/bedrock/DyeColorIdMap.php
index 0d10edad56a..a360e4f910a 100644
--- a/src/data/bedrock/DyeColorIdMap.php
+++ b/src/data/bedrock/DyeColorIdMap.php
@@ -26,6 +26,7 @@
 use pocketmine\block\utils\DyeColor;
 use pocketmine\data\bedrock\item\ItemTypeNames;
 use pocketmine\utils\SingletonTrait;
+use function spl_object_id;
 
 final class DyeColorIdMap{
 	use SingletonTrait;
@@ -47,28 +48,28 @@ final class DyeColorIdMap{
 	private array $enumToItemId = [];
 
 	private function __construct(){
-		$this->register(0, ItemTypeNames::WHITE_DYE, DyeColor::WHITE());
-		$this->register(1, ItemTypeNames::ORANGE_DYE, DyeColor::ORANGE());
-		$this->register(2, ItemTypeNames::MAGENTA_DYE, DyeColor::MAGENTA());
-		$this->register(3, ItemTypeNames::LIGHT_BLUE_DYE, DyeColor::LIGHT_BLUE());
-		$this->register(4, ItemTypeNames::YELLOW_DYE, DyeColor::YELLOW());
-		$this->register(5, ItemTypeNames::LIME_DYE, DyeColor::LIME());
-		$this->register(6, ItemTypeNames::PINK_DYE, DyeColor::PINK());
-		$this->register(7, ItemTypeNames::GRAY_DYE, DyeColor::GRAY());
-		$this->register(8, ItemTypeNames::LIGHT_GRAY_DYE, DyeColor::LIGHT_GRAY());
-		$this->register(9, ItemTypeNames::CYAN_DYE, DyeColor::CYAN());
-		$this->register(10, ItemTypeNames::PURPLE_DYE, DyeColor::PURPLE());
-		$this->register(11, ItemTypeNames::BLUE_DYE, DyeColor::BLUE());
-		$this->register(12, ItemTypeNames::BROWN_DYE, DyeColor::BROWN());
-		$this->register(13, ItemTypeNames::GREEN_DYE, DyeColor::GREEN());
-		$this->register(14, ItemTypeNames::RED_DYE, DyeColor::RED());
-		$this->register(15, ItemTypeNames::BLACK_DYE, DyeColor::BLACK());
+		$this->register(0, ItemTypeNames::WHITE_DYE, DyeColor::WHITE);
+		$this->register(1, ItemTypeNames::ORANGE_DYE, DyeColor::ORANGE);
+		$this->register(2, ItemTypeNames::MAGENTA_DYE, DyeColor::MAGENTA);
+		$this->register(3, ItemTypeNames::LIGHT_BLUE_DYE, DyeColor::LIGHT_BLUE);
+		$this->register(4, ItemTypeNames::YELLOW_DYE, DyeColor::YELLOW);
+		$this->register(5, ItemTypeNames::LIME_DYE, DyeColor::LIME);
+		$this->register(6, ItemTypeNames::PINK_DYE, DyeColor::PINK);
+		$this->register(7, ItemTypeNames::GRAY_DYE, DyeColor::GRAY);
+		$this->register(8, ItemTypeNames::LIGHT_GRAY_DYE, DyeColor::LIGHT_GRAY);
+		$this->register(9, ItemTypeNames::CYAN_DYE, DyeColor::CYAN);
+		$this->register(10, ItemTypeNames::PURPLE_DYE, DyeColor::PURPLE);
+		$this->register(11, ItemTypeNames::BLUE_DYE, DyeColor::BLUE);
+		$this->register(12, ItemTypeNames::BROWN_DYE, DyeColor::BROWN);
+		$this->register(13, ItemTypeNames::GREEN_DYE, DyeColor::GREEN);
+		$this->register(14, ItemTypeNames::RED_DYE, DyeColor::RED);
+		$this->register(15, ItemTypeNames::BLACK_DYE, DyeColor::BLACK);
 	}
 
 	private function register(int $id, string $itemId, DyeColor $color) : void{
 		$this->registerInt($id, $color);
 		$this->itemIdToEnum[$itemId] = $color;
-		$this->enumToItemId[$color->id()] = $itemId;
+		$this->enumToItemId[spl_object_id($color)] = $itemId;
 	}
 
 	public function toInvertedId(DyeColor $color) : int{
@@ -76,7 +77,7 @@ public function toInvertedId(DyeColor $color) : int{
 	}
 
 	public function toItemId(DyeColor $color) : string{
-		return $this->enumToItemId[$color->id()];
+		return $this->enumToItemId[spl_object_id($color)];
 	}
 
 	public function fromInvertedId(int $id) : ?DyeColor{
diff --git a/src/data/bedrock/MedicineTypeIdMap.php b/src/data/bedrock/MedicineTypeIdMap.php
index 2d9deb38021..00d1f27a866 100644
--- a/src/data/bedrock/MedicineTypeIdMap.php
+++ b/src/data/bedrock/MedicineTypeIdMap.php
@@ -32,9 +32,9 @@ final class MedicineTypeIdMap{
 	use IntSaveIdMapTrait;
 
 	private function __construct(){
-		$this->register(MedicineTypeIds::ANTIDOTE, MedicineType::ANTIDOTE());
-		$this->register(MedicineTypeIds::ELIXIR, MedicineType::ELIXIR());
-		$this->register(MedicineTypeIds::EYE_DROPS, MedicineType::EYE_DROPS());
-		$this->register(MedicineTypeIds::TONIC, MedicineType::TONIC());
+		$this->register(MedicineTypeIds::ANTIDOTE, MedicineType::ANTIDOTE);
+		$this->register(MedicineTypeIds::ELIXIR, MedicineType::ELIXIR);
+		$this->register(MedicineTypeIds::EYE_DROPS, MedicineType::EYE_DROPS);
+		$this->register(MedicineTypeIds::TONIC, MedicineType::TONIC);
 	}
 }
diff --git a/src/data/bedrock/MobHeadTypeIdMap.php b/src/data/bedrock/MobHeadTypeIdMap.php
index b99b1097bcc..ec678b19241 100644
--- a/src/data/bedrock/MobHeadTypeIdMap.php
+++ b/src/data/bedrock/MobHeadTypeIdMap.php
@@ -32,12 +32,12 @@ final class MobHeadTypeIdMap{
 	use IntSaveIdMapTrait;
 
 	private function __construct(){
-		$this->register(0, MobHeadType::SKELETON());
-		$this->register(1, MobHeadType::WITHER_SKELETON());
-		$this->register(2, MobHeadType::ZOMBIE());
-		$this->register(3, MobHeadType::PLAYER());
-		$this->register(4, MobHeadType::CREEPER());
-		$this->register(5, MobHeadType::DRAGON());
-		$this->register(6, MobHeadType::PIGLIN());
+		$this->register(0, MobHeadType::SKELETON);
+		$this->register(1, MobHeadType::WITHER_SKELETON);
+		$this->register(2, MobHeadType::ZOMBIE);
+		$this->register(3, MobHeadType::PLAYER);
+		$this->register(4, MobHeadType::CREEPER);
+		$this->register(5, MobHeadType::DRAGON);
+		$this->register(6, MobHeadType::PIGLIN);
 	}
 }
diff --git a/src/data/bedrock/MushroomBlockTypeIdMap.php b/src/data/bedrock/MushroomBlockTypeIdMap.php
index 927b52c7695..92edef4b23f 100644
--- a/src/data/bedrock/MushroomBlockTypeIdMap.php
+++ b/src/data/bedrock/MushroomBlockTypeIdMap.php
@@ -33,16 +33,16 @@ final class MushroomBlockTypeIdMap{
 	use IntSaveIdMapTrait;
 
 	public function __construct(){
-		$this->register(LegacyMeta::MUSHROOM_BLOCK_ALL_PORES, MushroomBlockType::PORES());
-		$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_NORTHWEST_CORNER, MushroomBlockType::CAP_NORTHWEST());
-		$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_NORTH_SIDE, MushroomBlockType::CAP_NORTH());
-		$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_NORTHEAST_CORNER, MushroomBlockType::CAP_NORTHEAST());
-		$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_WEST_SIDE, MushroomBlockType::CAP_WEST());
-		$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_TOP_ONLY, MushroomBlockType::CAP_MIDDLE());
-		$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_EAST_SIDE, MushroomBlockType::CAP_EAST());
-		$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHWEST_CORNER, MushroomBlockType::CAP_SOUTHWEST());
-		$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTH_SIDE, MushroomBlockType::CAP_SOUTH());
-		$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHEAST_CORNER, MushroomBlockType::CAP_SOUTHEAST());
-		$this->register(LegacyMeta::MUSHROOM_BLOCK_ALL_CAP, MushroomBlockType::ALL_CAP());
+		$this->register(LegacyMeta::MUSHROOM_BLOCK_ALL_PORES, MushroomBlockType::PORES);
+		$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_NORTHWEST_CORNER, MushroomBlockType::CAP_NORTHWEST);
+		$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_NORTH_SIDE, MushroomBlockType::CAP_NORTH);
+		$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_NORTHEAST_CORNER, MushroomBlockType::CAP_NORTHEAST);
+		$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_WEST_SIDE, MushroomBlockType::CAP_WEST);
+		$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_TOP_ONLY, MushroomBlockType::CAP_MIDDLE);
+		$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_EAST_SIDE, MushroomBlockType::CAP_EAST);
+		$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHWEST_CORNER, MushroomBlockType::CAP_SOUTHWEST);
+		$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTH_SIDE, MushroomBlockType::CAP_SOUTH);
+		$this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHEAST_CORNER, MushroomBlockType::CAP_SOUTHEAST);
+		$this->register(LegacyMeta::MUSHROOM_BLOCK_ALL_CAP, MushroomBlockType::ALL_CAP);
 	}
 }
diff --git a/src/data/bedrock/NoteInstrumentIdMap.php b/src/data/bedrock/NoteInstrumentIdMap.php
index 22d7e5cb1a5..e721b881d2e 100644
--- a/src/data/bedrock/NoteInstrumentIdMap.php
+++ b/src/data/bedrock/NoteInstrumentIdMap.php
@@ -32,21 +32,21 @@ final class NoteInstrumentIdMap{
 	use IntSaveIdMapTrait;
 
 	private function __construct(){
-		$this->register(0, NoteInstrument::PIANO());
-		$this->register(1, NoteInstrument::BASS_DRUM());
-		$this->register(2, NoteInstrument::SNARE());
-		$this->register(3, NoteInstrument::CLICKS_AND_STICKS());
-		$this->register(4, NoteInstrument::DOUBLE_BASS());
-		$this->register(5, NoteInstrument::BELL());
-		$this->register(6, NoteInstrument::FLUTE());
-		$this->register(7, NoteInstrument::CHIME());
-		$this->register(8, NoteInstrument::GUITAR());
-		$this->register(9, NoteInstrument::XYLOPHONE());
-		$this->register(10, NoteInstrument::IRON_XYLOPHONE());
-		$this->register(11, NoteInstrument::COW_BELL());
-		$this->register(12, NoteInstrument::DIDGERIDOO());
-		$this->register(13, NoteInstrument::BIT());
-		$this->register(14, NoteInstrument::BANJO());
-		$this->register(15, NoteInstrument::PLING());
+		$this->register(0, NoteInstrument::PIANO);
+		$this->register(1, NoteInstrument::BASS_DRUM);
+		$this->register(2, NoteInstrument::SNARE);
+		$this->register(3, NoteInstrument::CLICKS_AND_STICKS);
+		$this->register(4, NoteInstrument::DOUBLE_BASS);
+		$this->register(5, NoteInstrument::BELL);
+		$this->register(6, NoteInstrument::FLUTE);
+		$this->register(7, NoteInstrument::CHIME);
+		$this->register(8, NoteInstrument::GUITAR);
+		$this->register(9, NoteInstrument::XYLOPHONE);
+		$this->register(10, NoteInstrument::IRON_XYLOPHONE);
+		$this->register(11, NoteInstrument::COW_BELL);
+		$this->register(12, NoteInstrument::DIDGERIDOO);
+		$this->register(13, NoteInstrument::BIT);
+		$this->register(14, NoteInstrument::BANJO);
+		$this->register(15, NoteInstrument::PLING);
 	}
 }
diff --git a/src/data/bedrock/PotionTypeIdMap.php b/src/data/bedrock/PotionTypeIdMap.php
index 3e9858217ca..3fef20f68ce 100644
--- a/src/data/bedrock/PotionTypeIdMap.php
+++ b/src/data/bedrock/PotionTypeIdMap.php
@@ -32,48 +32,48 @@ final class PotionTypeIdMap{
 	use IntSaveIdMapTrait;
 
 	private function __construct(){
-		$this->register(PotionTypeIds::WATER, PotionType::WATER());
-		$this->register(PotionTypeIds::MUNDANE, PotionType::MUNDANE());
-		$this->register(PotionTypeIds::LONG_MUNDANE, PotionType::LONG_MUNDANE());
-		$this->register(PotionTypeIds::THICK, PotionType::THICK());
-		$this->register(PotionTypeIds::AWKWARD, PotionType::AWKWARD());
-		$this->register(PotionTypeIds::NIGHT_VISION, PotionType::NIGHT_VISION());
-		$this->register(PotionTypeIds::LONG_NIGHT_VISION, PotionType::LONG_NIGHT_VISION());
-		$this->register(PotionTypeIds::INVISIBILITY, PotionType::INVISIBILITY());
-		$this->register(PotionTypeIds::LONG_INVISIBILITY, PotionType::LONG_INVISIBILITY());
-		$this->register(PotionTypeIds::LEAPING, PotionType::LEAPING());
-		$this->register(PotionTypeIds::LONG_LEAPING, PotionType::LONG_LEAPING());
-		$this->register(PotionTypeIds::STRONG_LEAPING, PotionType::STRONG_LEAPING());
-		$this->register(PotionTypeIds::FIRE_RESISTANCE, PotionType::FIRE_RESISTANCE());
-		$this->register(PotionTypeIds::LONG_FIRE_RESISTANCE, PotionType::LONG_FIRE_RESISTANCE());
-		$this->register(PotionTypeIds::SWIFTNESS, PotionType::SWIFTNESS());
-		$this->register(PotionTypeIds::LONG_SWIFTNESS, PotionType::LONG_SWIFTNESS());
-		$this->register(PotionTypeIds::STRONG_SWIFTNESS, PotionType::STRONG_SWIFTNESS());
-		$this->register(PotionTypeIds::SLOWNESS, PotionType::SLOWNESS());
-		$this->register(PotionTypeIds::LONG_SLOWNESS, PotionType::LONG_SLOWNESS());
-		$this->register(PotionTypeIds::WATER_BREATHING, PotionType::WATER_BREATHING());
-		$this->register(PotionTypeIds::LONG_WATER_BREATHING, PotionType::LONG_WATER_BREATHING());
-		$this->register(PotionTypeIds::HEALING, PotionType::HEALING());
-		$this->register(PotionTypeIds::STRONG_HEALING, PotionType::STRONG_HEALING());
-		$this->register(PotionTypeIds::HARMING, PotionType::HARMING());
-		$this->register(PotionTypeIds::STRONG_HARMING, PotionType::STRONG_HARMING());
-		$this->register(PotionTypeIds::POISON, PotionType::POISON());
-		$this->register(PotionTypeIds::LONG_POISON, PotionType::LONG_POISON());
-		$this->register(PotionTypeIds::STRONG_POISON, PotionType::STRONG_POISON());
-		$this->register(PotionTypeIds::REGENERATION, PotionType::REGENERATION());
-		$this->register(PotionTypeIds::LONG_REGENERATION, PotionType::LONG_REGENERATION());
-		$this->register(PotionTypeIds::STRONG_REGENERATION, PotionType::STRONG_REGENERATION());
-		$this->register(PotionTypeIds::STRENGTH, PotionType::STRENGTH());
-		$this->register(PotionTypeIds::LONG_STRENGTH, PotionType::LONG_STRENGTH());
-		$this->register(PotionTypeIds::STRONG_STRENGTH, PotionType::STRONG_STRENGTH());
-		$this->register(PotionTypeIds::WEAKNESS, PotionType::WEAKNESS());
-		$this->register(PotionTypeIds::LONG_WEAKNESS, PotionType::LONG_WEAKNESS());
-		$this->register(PotionTypeIds::WITHER, PotionType::WITHER());
-		$this->register(PotionTypeIds::TURTLE_MASTER, PotionType::TURTLE_MASTER());
-		$this->register(PotionTypeIds::LONG_TURTLE_MASTER, PotionType::LONG_TURTLE_MASTER());
-		$this->register(PotionTypeIds::STRONG_TURTLE_MASTER, PotionType::STRONG_TURTLE_MASTER());
-		$this->register(PotionTypeIds::SLOW_FALLING, PotionType::SLOW_FALLING());
-		$this->register(PotionTypeIds::LONG_SLOW_FALLING, PotionType::LONG_SLOW_FALLING());
-		$this->register(PotionTypeIds::STRONG_SLOWNESS, PotionType::STRONG_SLOWNESS());
+		$this->register(PotionTypeIds::WATER, PotionType::WATER);
+		$this->register(PotionTypeIds::MUNDANE, PotionType::MUNDANE);
+		$this->register(PotionTypeIds::LONG_MUNDANE, PotionType::LONG_MUNDANE);
+		$this->register(PotionTypeIds::THICK, PotionType::THICK);
+		$this->register(PotionTypeIds::AWKWARD, PotionType::AWKWARD);
+		$this->register(PotionTypeIds::NIGHT_VISION, PotionType::NIGHT_VISION);
+		$this->register(PotionTypeIds::LONG_NIGHT_VISION, PotionType::LONG_NIGHT_VISION);
+		$this->register(PotionTypeIds::INVISIBILITY, PotionType::INVISIBILITY);
+		$this->register(PotionTypeIds::LONG_INVISIBILITY, PotionType::LONG_INVISIBILITY);
+		$this->register(PotionTypeIds::LEAPING, PotionType::LEAPING);
+		$this->register(PotionTypeIds::LONG_LEAPING, PotionType::LONG_LEAPING);
+		$this->register(PotionTypeIds::STRONG_LEAPING, PotionType::STRONG_LEAPING);
+		$this->register(PotionTypeIds::FIRE_RESISTANCE, PotionType::FIRE_RESISTANCE);
+		$this->register(PotionTypeIds::LONG_FIRE_RESISTANCE, PotionType::LONG_FIRE_RESISTANCE);
+		$this->register(PotionTypeIds::SWIFTNESS, PotionType::SWIFTNESS);
+		$this->register(PotionTypeIds::LONG_SWIFTNESS, PotionType::LONG_SWIFTNESS);
+		$this->register(PotionTypeIds::STRONG_SWIFTNESS, PotionType::STRONG_SWIFTNESS);
+		$this->register(PotionTypeIds::SLOWNESS, PotionType::SLOWNESS);
+		$this->register(PotionTypeIds::LONG_SLOWNESS, PotionType::LONG_SLOWNESS);
+		$this->register(PotionTypeIds::WATER_BREATHING, PotionType::WATER_BREATHING);
+		$this->register(PotionTypeIds::LONG_WATER_BREATHING, PotionType::LONG_WATER_BREATHING);
+		$this->register(PotionTypeIds::HEALING, PotionType::HEALING);
+		$this->register(PotionTypeIds::STRONG_HEALING, PotionType::STRONG_HEALING);
+		$this->register(PotionTypeIds::HARMING, PotionType::HARMING);
+		$this->register(PotionTypeIds::STRONG_HARMING, PotionType::STRONG_HARMING);
+		$this->register(PotionTypeIds::POISON, PotionType::POISON);
+		$this->register(PotionTypeIds::LONG_POISON, PotionType::LONG_POISON);
+		$this->register(PotionTypeIds::STRONG_POISON, PotionType::STRONG_POISON);
+		$this->register(PotionTypeIds::REGENERATION, PotionType::REGENERATION);
+		$this->register(PotionTypeIds::LONG_REGENERATION, PotionType::LONG_REGENERATION);
+		$this->register(PotionTypeIds::STRONG_REGENERATION, PotionType::STRONG_REGENERATION);
+		$this->register(PotionTypeIds::STRENGTH, PotionType::STRENGTH);
+		$this->register(PotionTypeIds::LONG_STRENGTH, PotionType::LONG_STRENGTH);
+		$this->register(PotionTypeIds::STRONG_STRENGTH, PotionType::STRONG_STRENGTH);
+		$this->register(PotionTypeIds::WEAKNESS, PotionType::WEAKNESS);
+		$this->register(PotionTypeIds::LONG_WEAKNESS, PotionType::LONG_WEAKNESS);
+		$this->register(PotionTypeIds::WITHER, PotionType::WITHER);
+		$this->register(PotionTypeIds::TURTLE_MASTER, PotionType::TURTLE_MASTER);
+		$this->register(PotionTypeIds::LONG_TURTLE_MASTER, PotionType::LONG_TURTLE_MASTER);
+		$this->register(PotionTypeIds::STRONG_TURTLE_MASTER, PotionType::STRONG_TURTLE_MASTER);
+		$this->register(PotionTypeIds::SLOW_FALLING, PotionType::SLOW_FALLING);
+		$this->register(PotionTypeIds::LONG_SLOW_FALLING, PotionType::LONG_SLOW_FALLING);
+		$this->register(PotionTypeIds::STRONG_SLOWNESS, PotionType::STRONG_SLOWNESS);
 	}
 }
diff --git a/src/data/bedrock/SuspiciousStewTypeIdMap.php b/src/data/bedrock/SuspiciousStewTypeIdMap.php
index 37d12151730..c4de4b742d5 100644
--- a/src/data/bedrock/SuspiciousStewTypeIdMap.php
+++ b/src/data/bedrock/SuspiciousStewTypeIdMap.php
@@ -32,15 +32,15 @@ final class SuspiciousStewTypeIdMap{
 	use IntSaveIdMapTrait;
 
 	private function __construct(){
-		$this->register(SuspiciousStewTypeIds::POPPY, SuspiciousStewType::POPPY());
-		$this->register(SuspiciousStewTypeIds::CORNFLOWER, SuspiciousStewType::CORNFLOWER());
-		$this->register(SuspiciousStewTypeIds::TULIP, SuspiciousStewType::TULIP());
-		$this->register(SuspiciousStewTypeIds::AZURE_BLUET, SuspiciousStewType::AZURE_BLUET());
-		$this->register(SuspiciousStewTypeIds::LILY_OF_THE_VALLEY, SuspiciousStewType::LILY_OF_THE_VALLEY());
-		$this->register(SuspiciousStewTypeIds::DANDELION, SuspiciousStewType::DANDELION());
-		$this->register(SuspiciousStewTypeIds::BLUE_ORCHID, SuspiciousStewType::BLUE_ORCHID());
-		$this->register(SuspiciousStewTypeIds::ALLIUM, SuspiciousStewType::ALLIUM());
-		$this->register(SuspiciousStewTypeIds::OXEYE_DAISY, SuspiciousStewType::OXEYE_DAISY());
-		$this->register(SuspiciousStewTypeIds::WITHER_ROSE, SuspiciousStewType::WITHER_ROSE());
+		$this->register(SuspiciousStewTypeIds::POPPY, SuspiciousStewType::POPPY);
+		$this->register(SuspiciousStewTypeIds::CORNFLOWER, SuspiciousStewType::CORNFLOWER);
+		$this->register(SuspiciousStewTypeIds::TULIP, SuspiciousStewType::TULIP);
+		$this->register(SuspiciousStewTypeIds::AZURE_BLUET, SuspiciousStewType::AZURE_BLUET);
+		$this->register(SuspiciousStewTypeIds::LILY_OF_THE_VALLEY, SuspiciousStewType::LILY_OF_THE_VALLEY);
+		$this->register(SuspiciousStewTypeIds::DANDELION, SuspiciousStewType::DANDELION);
+		$this->register(SuspiciousStewTypeIds::BLUE_ORCHID, SuspiciousStewType::BLUE_ORCHID);
+		$this->register(SuspiciousStewTypeIds::ALLIUM, SuspiciousStewType::ALLIUM);
+		$this->register(SuspiciousStewTypeIds::OXEYE_DAISY, SuspiciousStewType::OXEYE_DAISY);
+		$this->register(SuspiciousStewTypeIds::WITHER_ROSE, SuspiciousStewType::WITHER_ROSE);
 	}
 }
diff --git a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php
index cf3535097ec..ee15fb800a7 100644
--- a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php
+++ b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php
@@ -24,6 +24,7 @@
 namespace pocketmine\data\bedrock\block\convert;
 
 use pocketmine\block\ActivatorRail;
+use pocketmine\block\AmethystCluster;
 use pocketmine\block\Anvil;
 use pocketmine\block\Bamboo;
 use pocketmine\block\BambooSapling;
@@ -50,6 +51,7 @@
 use pocketmine\block\Chain;
 use pocketmine\block\ChemistryTable;
 use pocketmine\block\Chest;
+use pocketmine\block\ChiseledBookshelf;
 use pocketmine\block\ChorusFlower;
 use pocketmine\block\CocoaBlock;
 use pocketmine\block\Concrete;
@@ -63,6 +65,7 @@
 use pocketmine\block\DetectorRail;
 use pocketmine\block\Dirt;
 use pocketmine\block\Door;
+use pocketmine\block\DoublePitcherCrop;
 use pocketmine\block\DoublePlant;
 use pocketmine\block\DoubleTallGrass;
 use pocketmine\block\DyedCandle;
@@ -101,6 +104,7 @@
 use pocketmine\block\NetherVines;
 use pocketmine\block\NetherWartPlant;
 use pocketmine\block\PinkPetals;
+use pocketmine\block\PitcherCrop;
 use pocketmine\block\Potato;
 use pocketmine\block\PoweredRail;
 use pocketmine\block\PumpkinStem;
@@ -134,6 +138,7 @@
 use pocketmine\block\SweetBerryBush;
 use pocketmine\block\TNT;
 use pocketmine\block\Torch;
+use pocketmine\block\TorchflowerCrop;
 use pocketmine\block\Trapdoor;
 use pocketmine\block\TrappedChest;
 use pocketmine\block\Tripwire;
@@ -174,7 +179,6 @@
 use pocketmine\data\bedrock\block\convert\BlockStateWriter as Writer;
 use pocketmine\math\Axis;
 use pocketmine\math\Facing;
-use pocketmine\utils\AssumptionFailedError;
 use function get_class;
 
 final class BlockObjectToStateSerializer implements BlockStateSerializer{
@@ -271,241 +275,229 @@ public function serializeBlock(Block $blockState) : BlockStateData{
 	private function registerCandleSerializers() : void{
 		$this->map(Blocks::CANDLE(), fn(Candle $block) => Helper::encodeCandle($block, new Writer(Ids::CANDLE)));
 		$this->map(Blocks::DYED_CANDLE(), fn(DyedCandle $block) => Helper::encodeCandle($block, new Writer(match($block->getColor()){
-			DyeColor::BLACK() => Ids::BLACK_CANDLE,
-			DyeColor::BLUE() => Ids::BLUE_CANDLE,
-			DyeColor::BROWN() => Ids::BROWN_CANDLE,
-			DyeColor::CYAN() => Ids::CYAN_CANDLE,
-			DyeColor::GRAY() => Ids::GRAY_CANDLE,
-			DyeColor::GREEN() => Ids::GREEN_CANDLE,
-			DyeColor::LIGHT_BLUE() => Ids::LIGHT_BLUE_CANDLE,
-			DyeColor::LIGHT_GRAY() => Ids::LIGHT_GRAY_CANDLE,
-			DyeColor::LIME() => Ids::LIME_CANDLE,
-			DyeColor::MAGENTA() => Ids::MAGENTA_CANDLE,
-			DyeColor::ORANGE() => Ids::ORANGE_CANDLE,
-			DyeColor::PINK() => Ids::PINK_CANDLE,
-			DyeColor::PURPLE() => Ids::PURPLE_CANDLE,
-			DyeColor::RED() => Ids::RED_CANDLE,
-			DyeColor::WHITE() => Ids::WHITE_CANDLE,
-			DyeColor::YELLOW() => Ids::YELLOW_CANDLE,
-			default => throw new AssumptionFailedError("Unhandled DyeColor " . $block->getColor()->name())
+			DyeColor::BLACK => Ids::BLACK_CANDLE,
+			DyeColor::BLUE => Ids::BLUE_CANDLE,
+			DyeColor::BROWN => Ids::BROWN_CANDLE,
+			DyeColor::CYAN => Ids::CYAN_CANDLE,
+			DyeColor::GRAY => Ids::GRAY_CANDLE,
+			DyeColor::GREEN => Ids::GREEN_CANDLE,
+			DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_CANDLE,
+			DyeColor::LIGHT_GRAY => Ids::LIGHT_GRAY_CANDLE,
+			DyeColor::LIME => Ids::LIME_CANDLE,
+			DyeColor::MAGENTA => Ids::MAGENTA_CANDLE,
+			DyeColor::ORANGE => Ids::ORANGE_CANDLE,
+			DyeColor::PINK => Ids::PINK_CANDLE,
+			DyeColor::PURPLE => Ids::PURPLE_CANDLE,
+			DyeColor::RED => Ids::RED_CANDLE,
+			DyeColor::WHITE => Ids::WHITE_CANDLE,
+			DyeColor::YELLOW => Ids::YELLOW_CANDLE,
 		})));
 		$this->map(Blocks::CAKE_WITH_CANDLE(), fn(CakeWithCandle $block) => Writer::create(Ids::CANDLE_CAKE)
 			->writeBool(StateNames::LIT, $block->isLit()));
 		$this->map(Blocks::CAKE_WITH_DYED_CANDLE(), fn(CakeWithDyedCandle $block) => Writer::create(match($block->getColor()){
-			DyeColor::BLACK() => Ids::BLACK_CANDLE_CAKE,
-			DyeColor::BLUE() => Ids::BLUE_CANDLE_CAKE,
-			DyeColor::BROWN() => Ids::BROWN_CANDLE_CAKE,
-			DyeColor::CYAN() => Ids::CYAN_CANDLE_CAKE,
-			DyeColor::GRAY() => Ids::GRAY_CANDLE_CAKE,
-			DyeColor::GREEN() => Ids::GREEN_CANDLE_CAKE,
-			DyeColor::LIGHT_BLUE() => Ids::LIGHT_BLUE_CANDLE_CAKE,
-			DyeColor::LIGHT_GRAY() => Ids::LIGHT_GRAY_CANDLE_CAKE,
-			DyeColor::LIME() => Ids::LIME_CANDLE_CAKE,
-			DyeColor::MAGENTA() => Ids::MAGENTA_CANDLE_CAKE,
-			DyeColor::ORANGE() => Ids::ORANGE_CANDLE_CAKE,
-			DyeColor::PINK() => Ids::PINK_CANDLE_CAKE,
-			DyeColor::PURPLE() => Ids::PURPLE_CANDLE_CAKE,
-			DyeColor::RED() => Ids::RED_CANDLE_CAKE,
-			DyeColor::WHITE() => Ids::WHITE_CANDLE_CAKE,
-			DyeColor::YELLOW() => Ids::YELLOW_CANDLE_CAKE,
-			default => throw new AssumptionFailedError("Unhandled DyeColor " . $block->getColor()->name())
+			DyeColor::BLACK => Ids::BLACK_CANDLE_CAKE,
+			DyeColor::BLUE => Ids::BLUE_CANDLE_CAKE,
+			DyeColor::BROWN => Ids::BROWN_CANDLE_CAKE,
+			DyeColor::CYAN => Ids::CYAN_CANDLE_CAKE,
+			DyeColor::GRAY => Ids::GRAY_CANDLE_CAKE,
+			DyeColor::GREEN => Ids::GREEN_CANDLE_CAKE,
+			DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_CANDLE_CAKE,
+			DyeColor::LIGHT_GRAY => Ids::LIGHT_GRAY_CANDLE_CAKE,
+			DyeColor::LIME => Ids::LIME_CANDLE_CAKE,
+			DyeColor::MAGENTA => Ids::MAGENTA_CANDLE_CAKE,
+			DyeColor::ORANGE => Ids::ORANGE_CANDLE_CAKE,
+			DyeColor::PINK => Ids::PINK_CANDLE_CAKE,
+			DyeColor::PURPLE => Ids::PURPLE_CANDLE_CAKE,
+			DyeColor::RED => Ids::RED_CANDLE_CAKE,
+			DyeColor::WHITE => Ids::WHITE_CANDLE_CAKE,
+			DyeColor::YELLOW => Ids::YELLOW_CANDLE_CAKE,
 		})->writeBool(StateNames::LIT, $block->isLit()));
 	}
 
 	public function registerFlatColorBlockSerializers() : void{
 		$this->map(Blocks::GLAZED_TERRACOTTA(), function(GlazedTerracotta $block) : Writer{
-			return Writer::create(match($color = $block->getColor()){
-				DyeColor::BLACK() => Ids::BLACK_GLAZED_TERRACOTTA,
-				DyeColor::BLUE() => Ids::BLUE_GLAZED_TERRACOTTA,
-				DyeColor::BROWN() => Ids::BROWN_GLAZED_TERRACOTTA,
-				DyeColor::CYAN() => Ids::CYAN_GLAZED_TERRACOTTA,
-				DyeColor::GRAY() => Ids::GRAY_GLAZED_TERRACOTTA,
-				DyeColor::GREEN() => Ids::GREEN_GLAZED_TERRACOTTA,
-				DyeColor::LIGHT_BLUE() => Ids::LIGHT_BLUE_GLAZED_TERRACOTTA,
-				DyeColor::LIGHT_GRAY() => Ids::SILVER_GLAZED_TERRACOTTA,
-				DyeColor::LIME() => Ids::LIME_GLAZED_TERRACOTTA,
-				DyeColor::MAGENTA() => Ids::MAGENTA_GLAZED_TERRACOTTA,
-				DyeColor::ORANGE() => Ids::ORANGE_GLAZED_TERRACOTTA,
-				DyeColor::PINK() => Ids::PINK_GLAZED_TERRACOTTA,
-				DyeColor::PURPLE() => Ids::PURPLE_GLAZED_TERRACOTTA,
-				DyeColor::RED() => Ids::RED_GLAZED_TERRACOTTA,
-				DyeColor::WHITE() => Ids::WHITE_GLAZED_TERRACOTTA,
-				DyeColor::YELLOW() => Ids::YELLOW_GLAZED_TERRACOTTA,
-				default => throw new AssumptionFailedError("Unhandled dye colour " . $color->name())
+			return Writer::create(match($block->getColor()){
+				DyeColor::BLACK => Ids::BLACK_GLAZED_TERRACOTTA,
+				DyeColor::BLUE => Ids::BLUE_GLAZED_TERRACOTTA,
+				DyeColor::BROWN => Ids::BROWN_GLAZED_TERRACOTTA,
+				DyeColor::CYAN => Ids::CYAN_GLAZED_TERRACOTTA,
+				DyeColor::GRAY => Ids::GRAY_GLAZED_TERRACOTTA,
+				DyeColor::GREEN => Ids::GREEN_GLAZED_TERRACOTTA,
+				DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_GLAZED_TERRACOTTA,
+				DyeColor::LIGHT_GRAY => Ids::SILVER_GLAZED_TERRACOTTA,
+				DyeColor::LIME => Ids::LIME_GLAZED_TERRACOTTA,
+				DyeColor::MAGENTA => Ids::MAGENTA_GLAZED_TERRACOTTA,
+				DyeColor::ORANGE => Ids::ORANGE_GLAZED_TERRACOTTA,
+				DyeColor::PINK => Ids::PINK_GLAZED_TERRACOTTA,
+				DyeColor::PURPLE => Ids::PURPLE_GLAZED_TERRACOTTA,
+				DyeColor::RED => Ids::RED_GLAZED_TERRACOTTA,
+				DyeColor::WHITE => Ids::WHITE_GLAZED_TERRACOTTA,
+				DyeColor::YELLOW => Ids::YELLOW_GLAZED_TERRACOTTA,
 			})
 				->writeHorizontalFacing($block->getFacing());
 		});
 
-		$this->map(Blocks::WOOL(), fn(Wool $block) => Writer::create(match($color = $block->getColor()){
-			DyeColor::BLACK() => Ids::BLACK_WOOL,
-			DyeColor::BLUE() => Ids::BLUE_WOOL,
-			DyeColor::BROWN() => Ids::BROWN_WOOL,
-			DyeColor::CYAN() => Ids::CYAN_WOOL,
-			DyeColor::GRAY() => Ids::GRAY_WOOL,
-			DyeColor::GREEN() => Ids::GREEN_WOOL,
-			DyeColor::LIGHT_BLUE() => Ids::LIGHT_BLUE_WOOL,
-			DyeColor::LIGHT_GRAY() => Ids::LIGHT_GRAY_WOOL,
-			DyeColor::LIME() => Ids::LIME_WOOL,
-			DyeColor::MAGENTA() => Ids::MAGENTA_WOOL,
-			DyeColor::ORANGE() => Ids::ORANGE_WOOL,
-			DyeColor::PINK() => Ids::PINK_WOOL,
-			DyeColor::PURPLE() => Ids::PURPLE_WOOL,
-			DyeColor::RED() => Ids::RED_WOOL,
-			DyeColor::WHITE() => Ids::WHITE_WOOL,
-			DyeColor::YELLOW() => Ids::YELLOW_WOOL,
-			default => throw new AssumptionFailedError("Unhandled dye colour " . $color->name())
+		$this->map(Blocks::WOOL(), fn(Wool $block) => Writer::create(match($block->getColor()){
+			DyeColor::BLACK => Ids::BLACK_WOOL,
+			DyeColor::BLUE => Ids::BLUE_WOOL,
+			DyeColor::BROWN => Ids::BROWN_WOOL,
+			DyeColor::CYAN => Ids::CYAN_WOOL,
+			DyeColor::GRAY => Ids::GRAY_WOOL,
+			DyeColor::GREEN => Ids::GREEN_WOOL,
+			DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_WOOL,
+			DyeColor::LIGHT_GRAY => Ids::LIGHT_GRAY_WOOL,
+			DyeColor::LIME => Ids::LIME_WOOL,
+			DyeColor::MAGENTA => Ids::MAGENTA_WOOL,
+			DyeColor::ORANGE => Ids::ORANGE_WOOL,
+			DyeColor::PINK => Ids::PINK_WOOL,
+			DyeColor::PURPLE => Ids::PURPLE_WOOL,
+			DyeColor::RED => Ids::RED_WOOL,
+			DyeColor::WHITE => Ids::WHITE_WOOL,
+			DyeColor::YELLOW => Ids::YELLOW_WOOL,
 		}));
 
-		$this->map(Blocks::CARPET(), fn(Carpet $block) => Writer::create(match($color = $block->getColor()){
-			DyeColor::BLACK() => Ids::BLACK_CARPET,
-			DyeColor::BLUE() => Ids::BLUE_CARPET,
-			DyeColor::BROWN() => Ids::BROWN_CARPET,
-			DyeColor::CYAN() => Ids::CYAN_CARPET,
-			DyeColor::GRAY() => Ids::GRAY_CARPET,
-			DyeColor::GREEN() => Ids::GREEN_CARPET,
-			DyeColor::LIGHT_BLUE() => Ids::LIGHT_BLUE_CARPET,
-			DyeColor::LIGHT_GRAY() => Ids::LIGHT_GRAY_CARPET,
-			DyeColor::LIME() => Ids::LIME_CARPET,
-			DyeColor::MAGENTA() => Ids::MAGENTA_CARPET,
-			DyeColor::ORANGE() => Ids::ORANGE_CARPET,
-			DyeColor::PINK() => Ids::PINK_CARPET,
-			DyeColor::PURPLE() => Ids::PURPLE_CARPET,
-			DyeColor::RED() => Ids::RED_CARPET,
-			DyeColor::WHITE() => Ids::WHITE_CARPET,
-			DyeColor::YELLOW() => Ids::YELLOW_CARPET,
-			default => throw new AssumptionFailedError("Unhandled dye colour " . $color->name())
+		$this->map(Blocks::CARPET(), fn(Carpet $block) => Writer::create(match($block->getColor()){
+			DyeColor::BLACK => Ids::BLACK_CARPET,
+			DyeColor::BLUE => Ids::BLUE_CARPET,
+			DyeColor::BROWN => Ids::BROWN_CARPET,
+			DyeColor::CYAN => Ids::CYAN_CARPET,
+			DyeColor::GRAY => Ids::GRAY_CARPET,
+			DyeColor::GREEN => Ids::GREEN_CARPET,
+			DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_CARPET,
+			DyeColor::LIGHT_GRAY => Ids::LIGHT_GRAY_CARPET,
+			DyeColor::LIME => Ids::LIME_CARPET,
+			DyeColor::MAGENTA => Ids::MAGENTA_CARPET,
+			DyeColor::ORANGE => Ids::ORANGE_CARPET,
+			DyeColor::PINK => Ids::PINK_CARPET,
+			DyeColor::PURPLE => Ids::PURPLE_CARPET,
+			DyeColor::RED => Ids::RED_CARPET,
+			DyeColor::WHITE => Ids::WHITE_CARPET,
+			DyeColor::YELLOW => Ids::YELLOW_CARPET,
 		}));
 
-		$this->map(Blocks::DYED_SHULKER_BOX(), fn(DyedShulkerBox $block) => Writer::create(match($color = $block->getColor()){
-			DyeColor::BLACK() => Ids::BLACK_SHULKER_BOX,
-			DyeColor::BLUE() => Ids::BLUE_SHULKER_BOX,
-			DyeColor::BROWN() => Ids::BROWN_SHULKER_BOX,
-			DyeColor::CYAN() => Ids::CYAN_SHULKER_BOX,
-			DyeColor::GRAY() => Ids::GRAY_SHULKER_BOX,
-			DyeColor::GREEN() => Ids::GREEN_SHULKER_BOX,
-			DyeColor::LIGHT_BLUE() => Ids::LIGHT_BLUE_SHULKER_BOX,
-			DyeColor::LIGHT_GRAY() => Ids::LIGHT_GRAY_SHULKER_BOX,
-			DyeColor::LIME() => Ids::LIME_SHULKER_BOX,
-			DyeColor::MAGENTA() => Ids::MAGENTA_SHULKER_BOX,
-			DyeColor::ORANGE() => Ids::ORANGE_SHULKER_BOX,
-			DyeColor::PINK() => Ids::PINK_SHULKER_BOX,
-			DyeColor::PURPLE() => Ids::PURPLE_SHULKER_BOX,
-			DyeColor::RED() => Ids::RED_SHULKER_BOX,
-			DyeColor::WHITE() => Ids::WHITE_SHULKER_BOX,
-			DyeColor::YELLOW() => Ids::YELLOW_SHULKER_BOX,
-			default => throw new AssumptionFailedError("Unhandled dye colour " . $color->name())
+		$this->map(Blocks::DYED_SHULKER_BOX(), fn(DyedShulkerBox $block) => Writer::create(match($block->getColor()){
+			DyeColor::BLACK => Ids::BLACK_SHULKER_BOX,
+			DyeColor::BLUE => Ids::BLUE_SHULKER_BOX,
+			DyeColor::BROWN => Ids::BROWN_SHULKER_BOX,
+			DyeColor::CYAN => Ids::CYAN_SHULKER_BOX,
+			DyeColor::GRAY => Ids::GRAY_SHULKER_BOX,
+			DyeColor::GREEN => Ids::GREEN_SHULKER_BOX,
+			DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_SHULKER_BOX,
+			DyeColor::LIGHT_GRAY => Ids::LIGHT_GRAY_SHULKER_BOX,
+			DyeColor::LIME => Ids::LIME_SHULKER_BOX,
+			DyeColor::MAGENTA => Ids::MAGENTA_SHULKER_BOX,
+			DyeColor::ORANGE => Ids::ORANGE_SHULKER_BOX,
+			DyeColor::PINK => Ids::PINK_SHULKER_BOX,
+			DyeColor::PURPLE => Ids::PURPLE_SHULKER_BOX,
+			DyeColor::RED => Ids::RED_SHULKER_BOX,
+			DyeColor::WHITE => Ids::WHITE_SHULKER_BOX,
+			DyeColor::YELLOW => Ids::YELLOW_SHULKER_BOX,
 		}));
 
-		$this->map(Blocks::CONCRETE(), fn(Concrete $block) => Writer::create(match($color = $block->getColor()){
-			DyeColor::BLACK() => Ids::BLACK_CONCRETE,
-			DyeColor::BLUE() => Ids::BLUE_CONCRETE,
-			DyeColor::BROWN() => Ids::BROWN_CONCRETE,
-			DyeColor::CYAN() => Ids::CYAN_CONCRETE,
-			DyeColor::GRAY() => Ids::GRAY_CONCRETE,
-			DyeColor::GREEN() => Ids::GREEN_CONCRETE,
-			DyeColor::LIGHT_BLUE() => Ids::LIGHT_BLUE_CONCRETE,
-			DyeColor::LIGHT_GRAY() => Ids::LIGHT_GRAY_CONCRETE,
-			DyeColor::LIME() => Ids::LIME_CONCRETE,
-			DyeColor::MAGENTA() => Ids::MAGENTA_CONCRETE,
-			DyeColor::ORANGE() => Ids::ORANGE_CONCRETE,
-			DyeColor::PINK() => Ids::PINK_CONCRETE,
-			DyeColor::PURPLE() => Ids::PURPLE_CONCRETE,
-			DyeColor::RED() => Ids::RED_CONCRETE,
-			DyeColor::WHITE() => Ids::WHITE_CONCRETE,
-			DyeColor::YELLOW() => Ids::YELLOW_CONCRETE,
-			default => throw new AssumptionFailedError("Unhandled dye colour " . $color->name())
+		$this->map(Blocks::CONCRETE(), fn(Concrete $block) => Writer::create(match($block->getColor()){
+			DyeColor::BLACK => Ids::BLACK_CONCRETE,
+			DyeColor::BLUE => Ids::BLUE_CONCRETE,
+			DyeColor::BROWN => Ids::BROWN_CONCRETE,
+			DyeColor::CYAN => Ids::CYAN_CONCRETE,
+			DyeColor::GRAY => Ids::GRAY_CONCRETE,
+			DyeColor::GREEN => Ids::GREEN_CONCRETE,
+			DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_CONCRETE,
+			DyeColor::LIGHT_GRAY => Ids::LIGHT_GRAY_CONCRETE,
+			DyeColor::LIME => Ids::LIME_CONCRETE,
+			DyeColor::MAGENTA => Ids::MAGENTA_CONCRETE,
+			DyeColor::ORANGE => Ids::ORANGE_CONCRETE,
+			DyeColor::PINK => Ids::PINK_CONCRETE,
+			DyeColor::PURPLE => Ids::PURPLE_CONCRETE,
+			DyeColor::RED => Ids::RED_CONCRETE,
+			DyeColor::WHITE => Ids::WHITE_CONCRETE,
+			DyeColor::YELLOW => Ids::YELLOW_CONCRETE,
 		}));
 
-		$this->map(Blocks::CONCRETE_POWDER(), fn(ConcretePowder $block) => Writer::create(match($color = $block->getColor()){
-			DyeColor::BLACK() => Ids::BLACK_CONCRETE_POWDER,
-			DyeColor::BLUE() => Ids::BLUE_CONCRETE_POWDER,
-			DyeColor::BROWN() => Ids::BROWN_CONCRETE_POWDER,
-			DyeColor::CYAN() => Ids::CYAN_CONCRETE_POWDER,
-			DyeColor::GRAY() => Ids::GRAY_CONCRETE_POWDER,
-			DyeColor::GREEN() => Ids::GREEN_CONCRETE_POWDER,
-			DyeColor::LIGHT_BLUE() => Ids::LIGHT_BLUE_CONCRETE_POWDER,
-			DyeColor::LIGHT_GRAY() => Ids::LIGHT_GRAY_CONCRETE_POWDER,
-			DyeColor::LIME() => Ids::LIME_CONCRETE_POWDER,
-			DyeColor::MAGENTA() => Ids::MAGENTA_CONCRETE_POWDER,
-			DyeColor::ORANGE() => Ids::ORANGE_CONCRETE_POWDER,
-			DyeColor::PINK() => Ids::PINK_CONCRETE_POWDER,
-			DyeColor::PURPLE() => Ids::PURPLE_CONCRETE_POWDER,
-			DyeColor::RED() => Ids::RED_CONCRETE_POWDER,
-			DyeColor::WHITE() => Ids::WHITE_CONCRETE_POWDER,
-			DyeColor::YELLOW() => Ids::YELLOW_CONCRETE_POWDER,
-			default => throw new AssumptionFailedError("Unhandled dye colour " . $color->name())
+		$this->map(Blocks::CONCRETE_POWDER(), fn(ConcretePowder $block) => Writer::create(match($block->getColor()){
+			DyeColor::BLACK => Ids::BLACK_CONCRETE_POWDER,
+			DyeColor::BLUE => Ids::BLUE_CONCRETE_POWDER,
+			DyeColor::BROWN => Ids::BROWN_CONCRETE_POWDER,
+			DyeColor::CYAN => Ids::CYAN_CONCRETE_POWDER,
+			DyeColor::GRAY => Ids::GRAY_CONCRETE_POWDER,
+			DyeColor::GREEN => Ids::GREEN_CONCRETE_POWDER,
+			DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_CONCRETE_POWDER,
+			DyeColor::LIGHT_GRAY => Ids::LIGHT_GRAY_CONCRETE_POWDER,
+			DyeColor::LIME => Ids::LIME_CONCRETE_POWDER,
+			DyeColor::MAGENTA => Ids::MAGENTA_CONCRETE_POWDER,
+			DyeColor::ORANGE => Ids::ORANGE_CONCRETE_POWDER,
+			DyeColor::PINK => Ids::PINK_CONCRETE_POWDER,
+			DyeColor::PURPLE => Ids::PURPLE_CONCRETE_POWDER,
+			DyeColor::RED => Ids::RED_CONCRETE_POWDER,
+			DyeColor::WHITE => Ids::WHITE_CONCRETE_POWDER,
+			DyeColor::YELLOW => Ids::YELLOW_CONCRETE_POWDER,
 		}));
 
-		$this->map(Blocks::STAINED_CLAY(), fn(StainedHardenedClay $block) => Writer::create(match($color = $block->getColor()){
-			DyeColor::BLACK() => Ids::BLACK_TERRACOTTA,
-			DyeColor::BLUE() => Ids::BLUE_TERRACOTTA,
-			DyeColor::BROWN() => Ids::BROWN_TERRACOTTA,
-			DyeColor::CYAN() => Ids::CYAN_TERRACOTTA,
-			DyeColor::GRAY() => Ids::GRAY_TERRACOTTA,
-			DyeColor::GREEN() => Ids::GREEN_TERRACOTTA,
-			DyeColor::LIGHT_BLUE() => Ids::LIGHT_BLUE_TERRACOTTA,
-			DyeColor::LIGHT_GRAY() => Ids::LIGHT_GRAY_TERRACOTTA,
-			DyeColor::LIME() => Ids::LIME_TERRACOTTA,
-			DyeColor::MAGENTA() => Ids::MAGENTA_TERRACOTTA,
-			DyeColor::ORANGE() => Ids::ORANGE_TERRACOTTA,
-			DyeColor::PINK() => Ids::PINK_TERRACOTTA,
-			DyeColor::PURPLE() => Ids::PURPLE_TERRACOTTA,
-			DyeColor::RED() => Ids::RED_TERRACOTTA,
-			DyeColor::WHITE() => Ids::WHITE_TERRACOTTA,
-			DyeColor::YELLOW() => Ids::YELLOW_TERRACOTTA,
-			default => throw new AssumptionFailedError("Unhandled dye colour " . $color->name())
+		$this->map(Blocks::STAINED_CLAY(), fn(StainedHardenedClay $block) => Writer::create(match($block->getColor()){
+			DyeColor::BLACK => Ids::BLACK_TERRACOTTA,
+			DyeColor::BLUE => Ids::BLUE_TERRACOTTA,
+			DyeColor::BROWN => Ids::BROWN_TERRACOTTA,
+			DyeColor::CYAN => Ids::CYAN_TERRACOTTA,
+			DyeColor::GRAY => Ids::GRAY_TERRACOTTA,
+			DyeColor::GREEN => Ids::GREEN_TERRACOTTA,
+			DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_TERRACOTTA,
+			DyeColor::LIGHT_GRAY => Ids::LIGHT_GRAY_TERRACOTTA,
+			DyeColor::LIME => Ids::LIME_TERRACOTTA,
+			DyeColor::MAGENTA => Ids::MAGENTA_TERRACOTTA,
+			DyeColor::ORANGE => Ids::ORANGE_TERRACOTTA,
+			DyeColor::PINK => Ids::PINK_TERRACOTTA,
+			DyeColor::PURPLE => Ids::PURPLE_TERRACOTTA,
+			DyeColor::RED => Ids::RED_TERRACOTTA,
+			DyeColor::WHITE => Ids::WHITE_TERRACOTTA,
+			DyeColor::YELLOW => Ids::YELLOW_TERRACOTTA,
 		}));
 
-		$this->map(Blocks::STAINED_GLASS(), fn(StainedGlass $block) => Writer::create(match($color = $block->getColor()){
-			DyeColor::BLACK() => Ids::BLACK_STAINED_GLASS,
-			DyeColor::BLUE() => Ids::BLUE_STAINED_GLASS,
-			DyeColor::BROWN() => Ids::BROWN_STAINED_GLASS,
-			DyeColor::CYAN() => Ids::CYAN_STAINED_GLASS,
-			DyeColor::GRAY() => Ids::GRAY_STAINED_GLASS,
-			DyeColor::GREEN() => Ids::GREEN_STAINED_GLASS,
-			DyeColor::LIGHT_BLUE() => Ids::LIGHT_BLUE_STAINED_GLASS,
-			DyeColor::LIGHT_GRAY() => Ids::LIGHT_GRAY_STAINED_GLASS,
-			DyeColor::LIME() => Ids::LIME_STAINED_GLASS,
-			DyeColor::MAGENTA() => Ids::MAGENTA_STAINED_GLASS,
-			DyeColor::ORANGE() => Ids::ORANGE_STAINED_GLASS,
-			DyeColor::PINK() => Ids::PINK_STAINED_GLASS,
-			DyeColor::PURPLE() => Ids::PURPLE_STAINED_GLASS,
-			DyeColor::RED() => Ids::RED_STAINED_GLASS,
-			DyeColor::WHITE() => Ids::WHITE_STAINED_GLASS,
-			DyeColor::YELLOW() => Ids::YELLOW_STAINED_GLASS,
-			default => throw new AssumptionFailedError("Unhandled dye colour " . $color->name())
+		$this->map(Blocks::STAINED_GLASS(), fn(StainedGlass $block) => Writer::create(match($block->getColor()){
+			DyeColor::BLACK => Ids::BLACK_STAINED_GLASS,
+			DyeColor::BLUE => Ids::BLUE_STAINED_GLASS,
+			DyeColor::BROWN => Ids::BROWN_STAINED_GLASS,
+			DyeColor::CYAN => Ids::CYAN_STAINED_GLASS,
+			DyeColor::GRAY => Ids::GRAY_STAINED_GLASS,
+			DyeColor::GREEN => Ids::GREEN_STAINED_GLASS,
+			DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_STAINED_GLASS,
+			DyeColor::LIGHT_GRAY => Ids::LIGHT_GRAY_STAINED_GLASS,
+			DyeColor::LIME => Ids::LIME_STAINED_GLASS,
+			DyeColor::MAGENTA => Ids::MAGENTA_STAINED_GLASS,
+			DyeColor::ORANGE => Ids::ORANGE_STAINED_GLASS,
+			DyeColor::PINK => Ids::PINK_STAINED_GLASS,
+			DyeColor::PURPLE => Ids::PURPLE_STAINED_GLASS,
+			DyeColor::RED => Ids::RED_STAINED_GLASS,
+			DyeColor::WHITE => Ids::WHITE_STAINED_GLASS,
+			DyeColor::YELLOW => Ids::YELLOW_STAINED_GLASS,
 		}));
 
-		$this->map(Blocks::STAINED_GLASS_PANE(), fn(StainedGlassPane $block) => Writer::create(match($color = $block->getColor()){
-			DyeColor::BLACK() => Ids::BLACK_STAINED_GLASS_PANE,
-			DyeColor::BLUE() => Ids::BLUE_STAINED_GLASS_PANE,
-			DyeColor::BROWN() => Ids::BROWN_STAINED_GLASS_PANE,
-			DyeColor::CYAN() => Ids::CYAN_STAINED_GLASS_PANE,
-			DyeColor::GRAY() => Ids::GRAY_STAINED_GLASS_PANE,
-			DyeColor::GREEN() => Ids::GREEN_STAINED_GLASS_PANE,
-			DyeColor::LIGHT_BLUE() => Ids::LIGHT_BLUE_STAINED_GLASS_PANE,
-			DyeColor::LIGHT_GRAY() => Ids::LIGHT_GRAY_STAINED_GLASS_PANE,
-			DyeColor::LIME() => Ids::LIME_STAINED_GLASS_PANE,
-			DyeColor::MAGENTA() => Ids::MAGENTA_STAINED_GLASS_PANE,
-			DyeColor::ORANGE() => Ids::ORANGE_STAINED_GLASS_PANE,
-			DyeColor::PINK() => Ids::PINK_STAINED_GLASS_PANE,
-			DyeColor::PURPLE() => Ids::PURPLE_STAINED_GLASS_PANE,
-			DyeColor::RED() => Ids::RED_STAINED_GLASS_PANE,
-			DyeColor::WHITE() => Ids::WHITE_STAINED_GLASS_PANE,
-			DyeColor::YELLOW() => Ids::YELLOW_STAINED_GLASS_PANE,
-			default => throw new AssumptionFailedError("Unhandled dye colour " . $color->name())
+		$this->map(Blocks::STAINED_GLASS_PANE(), fn(StainedGlassPane $block) => Writer::create(match($block->getColor()){
+			DyeColor::BLACK => Ids::BLACK_STAINED_GLASS_PANE,
+			DyeColor::BLUE => Ids::BLUE_STAINED_GLASS_PANE,
+			DyeColor::BROWN => Ids::BROWN_STAINED_GLASS_PANE,
+			DyeColor::CYAN => Ids::CYAN_STAINED_GLASS_PANE,
+			DyeColor::GRAY => Ids::GRAY_STAINED_GLASS_PANE,
+			DyeColor::GREEN => Ids::GREEN_STAINED_GLASS_PANE,
+			DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_STAINED_GLASS_PANE,
+			DyeColor::LIGHT_GRAY => Ids::LIGHT_GRAY_STAINED_GLASS_PANE,
+			DyeColor::LIME => Ids::LIME_STAINED_GLASS_PANE,
+			DyeColor::MAGENTA => Ids::MAGENTA_STAINED_GLASS_PANE,
+			DyeColor::ORANGE => Ids::ORANGE_STAINED_GLASS_PANE,
+			DyeColor::PINK => Ids::PINK_STAINED_GLASS_PANE,
+			DyeColor::PURPLE => Ids::PURPLE_STAINED_GLASS_PANE,
+			DyeColor::RED => Ids::RED_STAINED_GLASS_PANE,
+			DyeColor::WHITE => Ids::WHITE_STAINED_GLASS_PANE,
+			DyeColor::YELLOW => Ids::YELLOW_STAINED_GLASS_PANE,
 		}));
 	}
 
 	private function registerFlatCoralSerializers() : void{
 		$this->map(Blocks::CORAL(), fn(Coral $block) => Writer::create(
-			match($coralType = $block->getCoralType()){
-				CoralType::BRAIN() => $block->isDead() ? Ids::DEAD_BRAIN_CORAL : Ids::BRAIN_CORAL,
-				CoralType::BUBBLE() => $block->isDead() ? Ids::DEAD_BUBBLE_CORAL : Ids::BUBBLE_CORAL,
-				CoralType::FIRE() => $block->isDead() ? Ids::DEAD_FIRE_CORAL : Ids::FIRE_CORAL,
-				CoralType::HORN() => $block->isDead() ? Ids::DEAD_HORN_CORAL : Ids::HORN_CORAL,
-				CoralType::TUBE() => $block->isDead() ? Ids::DEAD_TUBE_CORAL : Ids::TUBE_CORAL,
-				default => throw new AssumptionFailedError("Unhandled coral type " . $coralType->name())
+			match($block->getCoralType()){
+				CoralType::BRAIN => $block->isDead() ? Ids::DEAD_BRAIN_CORAL : Ids::BRAIN_CORAL,
+				CoralType::BUBBLE => $block->isDead() ? Ids::DEAD_BUBBLE_CORAL : Ids::BUBBLE_CORAL,
+				CoralType::FIRE => $block->isDead() ? Ids::DEAD_FIRE_CORAL : Ids::FIRE_CORAL,
+				CoralType::HORN => $block->isDead() ? Ids::DEAD_HORN_CORAL : Ids::HORN_CORAL,
+				CoralType::TUBE => $block->isDead() ? Ids::DEAD_TUBE_CORAL : Ids::TUBE_CORAL,
 			}
 		));
 	}
@@ -731,6 +723,7 @@ private function registerSimpleSerializers() : void{
 		$this->mapSimple(Blocks::BOOKSHELF(), Ids::BOOKSHELF);
 		$this->mapSimple(Blocks::BRICKS(), Ids::BRICK_BLOCK);
 		$this->mapSimple(Blocks::BROWN_MUSHROOM(), Ids::BROWN_MUSHROOM);
+		$this->mapSimple(Blocks::BUDDING_AMETHYST(), Ids::BUDDING_AMETHYST);
 		$this->mapSimple(Blocks::CALCITE(), Ids::CALCITE);
 		$this->mapSimple(Blocks::CARTOGRAPHY_TABLE(), Ids::CARTOGRAPHY_TABLE);
 		$this->mapSimple(Blocks::CHEMICAL_HEAT(), Ids::CHEMICAL_HEAT);
@@ -750,6 +743,7 @@ private function registerSimpleSerializers() : void{
 		$this->mapSimple(Blocks::CRACKED_NETHER_BRICKS(), Ids::CRACKED_NETHER_BRICKS);
 		$this->mapSimple(Blocks::CRACKED_POLISHED_BLACKSTONE_BRICKS(), Ids::CRACKED_POLISHED_BLACKSTONE_BRICKS);
 		$this->mapSimple(Blocks::CRAFTING_TABLE(), Ids::CRAFTING_TABLE);
+		$this->mapSimple(Blocks::CRIMSON_ROOTS(), Ids::CRIMSON_ROOTS);
 		$this->mapSimple(Blocks::CRYING_OBSIDIAN(), Ids::CRYING_OBSIDIAN);
 		$this->mapSimple(Blocks::DANDELION(), Ids::YELLOW_FLOWER);
 		$this->mapSimple(Blocks::DEAD_BUSH(), Ids::DEADBUSH);
@@ -964,8 +958,10 @@ private function registerSimpleSerializers() : void{
 		$this->mapSimple(Blocks::SOUL_SOIL(), Ids::SOUL_SOIL);
 		$this->mapSimple(Blocks::SPORE_BLOSSOM(), Ids::SPORE_BLOSSOM);
 		$this->mapSimple(Blocks::TINTED_GLASS(), Ids::TINTED_GLASS);
+		$this->mapSimple(Blocks::TORCHFLOWER(), Ids::TORCHFLOWER);
 		$this->mapSimple(Blocks::TUFF(), Ids::TUFF);
 		$this->mapSimple(Blocks::WARPED_WART_BLOCK(), Ids::WARPED_WART_BLOCK);
+		$this->mapSimple(Blocks::WARPED_ROOTS(), Ids::WARPED_ROOTS);
 		$this->mapSimple(Blocks::WITHER_ROSE(), Ids::WITHER_ROSE);
 	}
 
@@ -979,6 +975,16 @@ private function registerSerializers() : void{
 		$this->map(Blocks::ALLIUM(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_ALLIUM));
 		$this->map(Blocks::ALL_SIDED_MUSHROOM_STEM(), fn() => Writer::create(Ids::BROWN_MUSHROOM_BLOCK)
 				->writeInt(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM));
+		$this->map(Blocks::AMETHYST_CLUSTER(), fn(AmethystCluster $block) => Writer::create(
+			match($stage = $block->getStage()){
+				AmethystCluster::STAGE_SMALL_BUD => Ids::SMALL_AMETHYST_BUD,
+				AmethystCluster::STAGE_MEDIUM_BUD => Ids::MEDIUM_AMETHYST_BUD,
+				AmethystCluster::STAGE_LARGE_BUD => Ids::LARGE_AMETHYST_BUD,
+				AmethystCluster::STAGE_CLUSTER => Ids::AMETHYST_CLUSTER,
+				default => throw new BlockStateSerializeException("Invalid Amethyst Cluster stage $stage"),
+			})
+			->writeBlockFace($block->getFacing())
+		);
 		$this->map(Blocks::ANDESITE(), fn() => Helper::encodeStone(StringValues::STONE_TYPE_ANDESITE));
 		$this->map(Blocks::ANDESITE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_ANDESITE));
 		$this->map(Blocks::ANDESITE_STAIRS(), fn(Stair $block) => Helper::encodeStairs($block, new Writer(Ids::ANDESITE_STAIRS)));
@@ -1046,12 +1052,11 @@ private function registerSerializers() : void{
 		$this->map(Blocks::BIG_DRIPLEAF_HEAD(), function(BigDripleafHead $block) : Writer{
 			return Writer::create(Ids::BIG_DRIPLEAF)
 				->writeCardinalHorizontalFacing($block->getFacing())
-				->writeString(StateNames::BIG_DRIPLEAF_TILT, match($block->getLeafState()->id()){
-					DripleafState::STABLE()->id() => StringValues::BIG_DRIPLEAF_TILT_NONE,
-					DripleafState::UNSTABLE()->id() => StringValues::BIG_DRIPLEAF_TILT_UNSTABLE,
-					DripleafState::PARTIAL_TILT()->id() => StringValues::BIG_DRIPLEAF_TILT_PARTIAL_TILT,
-					DripleafState::FULL_TILT()->id() => StringValues::BIG_DRIPLEAF_TILT_FULL_TILT,
-					default => throw new BlockStateSerializeException("Invalid Dripleaf tilt type " . $block->getLeafState()->name())
+				->writeString(StateNames::BIG_DRIPLEAF_TILT, match($block->getLeafState()){
+					DripleafState::STABLE => StringValues::BIG_DRIPLEAF_TILT_NONE,
+					DripleafState::UNSTABLE => StringValues::BIG_DRIPLEAF_TILT_UNSTABLE,
+					DripleafState::PARTIAL_TILT => StringValues::BIG_DRIPLEAF_TILT_PARTIAL_TILT,
+					DripleafState::FULL_TILT => StringValues::BIG_DRIPLEAF_TILT_FULL_TILT,
 				})
 				->writeBool(StateNames::BIG_DRIPLEAF_HEAD, true);
 		});
@@ -1075,9 +1080,9 @@ private function registerSerializers() : void{
 		});
 		$this->map(Blocks::BREWING_STAND(), function(BrewingStand $block) : Writer{
 			return Writer::create(Ids::BREWING_STAND)
-				->writeBool(StateNames::BREWING_STAND_SLOT_A_BIT, $block->hasSlot(BrewingStandSlot::EAST()))
-				->writeBool(StateNames::BREWING_STAND_SLOT_B_BIT, $block->hasSlot(BrewingStandSlot::SOUTHWEST()))
-				->writeBool(StateNames::BREWING_STAND_SLOT_C_BIT, $block->hasSlot(BrewingStandSlot::NORTHWEST()));
+				->writeBool(StateNames::BREWING_STAND_SLOT_A_BIT, $block->hasSlot(BrewingStandSlot::EAST))
+				->writeBool(StateNames::BREWING_STAND_SLOT_B_BIT, $block->hasSlot(BrewingStandSlot::SOUTHWEST))
+				->writeBool(StateNames::BREWING_STAND_SLOT_C_BIT, $block->hasSlot(BrewingStandSlot::NORTHWEST));
 		});
 		$this->map(Blocks::BRICK_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab1($block, StringValues::STONE_SLAB_TYPE_BRICK));
 		$this->mapStairs(Blocks::BRICK_STAIRS(), Ids::BRICK_STAIRS);
@@ -1115,6 +1120,15 @@ private function registerSerializers() : void{
 			return Writer::create(Ids::CHEST)
 				->writeHorizontalFacing($block->getFacing());
 		});
+		$this->map(Blocks::CHISELED_BOOKSHELF(), function(ChiseledBookshelf $block) : Writer{
+			$flags = 0;
+			foreach($block->getSlots() as $slot){
+				$flags |= 1 << $slot->value;
+			}
+			return Writer::create(Ids::CHISELED_BOOKSHELF)
+				->writeLegacyHorizontalFacing($block->getFacing())
+				->writeInt(StateNames::BOOKS_STORED, $flags);
+		});
 		$this->map(Blocks::CHISELED_QUARTZ(), fn(SimplePillar $block) => Helper::encodeQuartz(StringValues::CHISEL_TYPE_CHISELED, $block->getAxis()));
 		$this->map(Blocks::CHISELED_RED_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::RED_SANDSTONE, StringValues::SAND_STONE_TYPE_HEIROGLYPHS));
 		$this->map(Blocks::CHISELED_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::SANDSTONE, StringValues::SAND_STONE_TYPE_HEIROGLYPHS));
@@ -1260,14 +1274,14 @@ private function registerSerializers() : void{
 		$this->map(Blocks::DIORITE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_DIORITE));
 		$this->map(Blocks::DIRT(), function(Dirt $block) : Writer{
 			$dirtType = $block->getDirtType();
-			if($dirtType->equals(DirtType::ROOTED())){
+			if($dirtType === DirtType::ROOTED){
 				return new Writer(Ids::DIRT_WITH_ROOTS);
 			}
 			return Writer::create(Ids::DIRT)
 				->writeString(StateNames::DIRT_TYPE, match($dirtType){
-					DirtType::COARSE() => StringValues::DIRT_TYPE_COARSE,
-					DirtType::NORMAL() => StringValues::DIRT_TYPE_NORMAL,
-					default => throw new AssumptionFailedError("Unhandled dirt type " . $dirtType->name())
+					DirtType::COARSE => StringValues::DIRT_TYPE_COARSE,
+					DirtType::NORMAL => StringValues::DIRT_TYPE_NORMAL,
+					//ROOTED was already checked above
 				});
 		});
 		$this->map(Blocks::DOUBLE_TALLGRASS(), fn(DoubleTallGrass $block) => Helper::encodeDoublePlant($block, StringValues::DOUBLE_PLANT_TYPE_GRASS, Writer::create(Ids::DOUBLE_PLANT)));
@@ -1305,10 +1319,9 @@ private function registerSerializers() : void{
 		});
 		$this->map(Blocks::FROGLIGHT(), function(Froglight $block){
 			return Writer::create(match($block->getFroglightType()){
-				FroglightType::OCHRE() => Ids::OCHRE_FROGLIGHT,
-				FroglightType::PEARLESCENT() => Ids::PEARLESCENT_FROGLIGHT,
-				FroglightType::VERDANT() => Ids::VERDANT_FROGLIGHT,
-				default => throw new AssumptionFailedError("Unhandled froglight type " . $block->getFroglightType()->name())
+				FroglightType::OCHRE => Ids::OCHRE_FROGLIGHT,
+				FroglightType::PEARLESCENT => Ids::PEARLESCENT_FROGLIGHT,
+				FroglightType::VERDANT => Ids::VERDANT_FROGLIGHT,
 			})
 				->writePillarAxis($block->getAxis());
 		});
@@ -1372,16 +1385,15 @@ private function registerSerializers() : void{
 		$this->map(Blocks::LEVER(), function(Lever $block) : Writer{
 			return Writer::create(Ids::LEVER)
 				->writeBool(StateNames::OPEN_BIT, $block->isActivated())
-				->writeString(StateNames::LEVER_DIRECTION, match($block->getFacing()->id()){
-					LeverFacing::DOWN_AXIS_Z()->id() => StringValues::LEVER_DIRECTION_DOWN_NORTH_SOUTH,
-					LeverFacing::DOWN_AXIS_X()->id() => StringValues::LEVER_DIRECTION_DOWN_EAST_WEST,
-					LeverFacing::UP_AXIS_Z()->id() => StringValues::LEVER_DIRECTION_UP_NORTH_SOUTH,
-					LeverFacing::UP_AXIS_X()->id() => StringValues::LEVER_DIRECTION_UP_EAST_WEST,
-					LeverFacing::NORTH()->id() => StringValues::LEVER_DIRECTION_NORTH,
-					LeverFacing::SOUTH()->id() => StringValues::LEVER_DIRECTION_SOUTH,
-					LeverFacing::WEST()->id() => StringValues::LEVER_DIRECTION_WEST,
-					LeverFacing::EAST()->id() => StringValues::LEVER_DIRECTION_EAST,
-					default => throw new BlockStateSerializeException("Invalid Lever facing " . $block->getFacing()->name()),
+				->writeString(StateNames::LEVER_DIRECTION, match($block->getFacing()){
+					LeverFacing::DOWN_AXIS_Z => StringValues::LEVER_DIRECTION_DOWN_NORTH_SOUTH,
+					LeverFacing::DOWN_AXIS_X => StringValues::LEVER_DIRECTION_DOWN_EAST_WEST,
+					LeverFacing::UP_AXIS_Z => StringValues::LEVER_DIRECTION_UP_NORTH_SOUTH,
+					LeverFacing::UP_AXIS_X => StringValues::LEVER_DIRECTION_UP_EAST_WEST,
+					LeverFacing::NORTH => StringValues::LEVER_DIRECTION_NORTH,
+					LeverFacing::SOUTH => StringValues::LEVER_DIRECTION_SOUTH,
+					LeverFacing::WEST => StringValues::LEVER_DIRECTION_WEST,
+					LeverFacing::EAST => StringValues::LEVER_DIRECTION_EAST,
 				});
 		});
 		$this->map(Blocks::LIGHT(), function(Light $block) : Writer{
@@ -1447,6 +1459,20 @@ private function registerSerializers() : void{
 				->writeInt(StateNames::GROWTH, $block->getCount() - 1);
 		});
 		$this->map(Blocks::PINK_TULIP(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_TULIP_PINK));
+		$this->map(Blocks::PITCHER_PLANT(), function(DoublePlant $block) : Writer{
+			return Writer::create(Ids::PITCHER_PLANT)
+				->writeBool(StateNames::UPPER_BLOCK_BIT, $block->isTop());
+		});
+		$this->map(Blocks::PITCHER_CROP(), function(PitcherCrop $block) : Writer{
+			return Writer::create(Ids::PITCHER_CROP)
+				->writeInt(StateNames::GROWTH, $block->getAge())
+				->writeBool(StateNames::UPPER_BLOCK_BIT, false);
+		});
+		$this->map(Blocks::DOUBLE_PITCHER_CROP(), function(DoublePitcherCrop $block) : Writer{
+			return Writer::create(Ids::PITCHER_CROP)
+				->writeInt(StateNames::GROWTH, $block->getAge() + 1 + PitcherCrop::MAX_AGE)
+				->writeBool(StateNames::UPPER_BLOCK_BIT, $block->isTop());
+		});
 		$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));
 		$this->mapStairs(Blocks::POLISHED_ANDESITE_STAIRS(), Ids::POLISHED_ANDESITE_STAIRS);
@@ -1635,6 +1661,10 @@ private function registerSerializers() : void{
 			return Writer::create(Ids::TORCH)
 				->writeTorchFacing($block->getFacing());
 		});
+		$this->map(Blocks::TORCHFLOWER_CROP(), function(TorchflowerCrop $block){
+			return Writer::create(Ids::TORCHFLOWER_CROP)
+				->writeInt(StateNames::GROWTH, $block->isReady() ? 1 : 0);
+		});
 		$this->map(Blocks::TRAPPED_CHEST(), function(TrappedChest $block) : Writer{
 			return Writer::create(Ids::TRAPPED_CHEST)
 				->writeHorizontalFacing($block->getFacing());
@@ -1670,13 +1700,12 @@ private function registerSerializers() : void{
 		});
 		$this->map(Blocks::WALL_CORAL_FAN(), function(WallCoralFan $block) : Writer{
 			$coralType = $block->getCoralType();
-			return Writer::create(match($coralType->id()){
-				CoralType::TUBE()->id(), CoralType::BRAIN()->id() => Ids::CORAL_FAN_HANG,
-				CoralType::BUBBLE()->id(), CoralType::FIRE()->id() => Ids::CORAL_FAN_HANG2,
-				CoralType::HORN()->id() => Ids::CORAL_FAN_HANG3,
-				default => throw new BlockStateSerializeException("Invalid Coral type " . $coralType->name()),
+			return Writer::create(match($coralType){
+				CoralType::TUBE, CoralType::BRAIN => Ids::CORAL_FAN_HANG,
+				CoralType::BUBBLE, CoralType::FIRE => Ids::CORAL_FAN_HANG2,
+				CoralType::HORN => Ids::CORAL_FAN_HANG3,
 			})
-				->writeBool(StateNames::CORAL_HANG_TYPE_BIT, $coralType->equals(CoralType::BRAIN()) || $coralType->equals(CoralType::FIRE()))
+				->writeBool(StateNames::CORAL_HANG_TYPE_BIT, $coralType === CoralType::BRAIN || $coralType === CoralType::FIRE)
 				->writeBool(StateNames::DEAD_BIT, $block->isDead())
 				->writeCoralFacing($block->getFacing());
 		});
diff --git a/src/data/bedrock/block/convert/BlockStateReader.php b/src/data/bedrock/block/convert/BlockStateReader.php
index 33718a686f5..6bb0c8bf819 100644
--- a/src/data/bedrock/block/convert/BlockStateReader.php
+++ b/src/data/bedrock/block/convert/BlockStateReader.php
@@ -135,6 +135,19 @@ public function readFacingDirection() : int{
 		]);
 	}
 
+	/** @throws BlockStateDeserializeException */
+	public function readBlockFace() : int{
+		return match($raw = $this->readString(BlockStateNames::MC_BLOCK_FACE)){
+			StringValues::MC_BLOCK_FACE_DOWN => Facing::DOWN,
+			StringValues::MC_BLOCK_FACE_UP => Facing::UP,
+			StringValues::MC_BLOCK_FACE_NORTH => Facing::NORTH,
+			StringValues::MC_BLOCK_FACE_SOUTH => Facing::SOUTH,
+			StringValues::MC_BLOCK_FACE_WEST => Facing::WEST,
+			StringValues::MC_BLOCK_FACE_EAST => Facing::EAST,
+			default => throw $this->badValueException(BlockStateNames::MC_BLOCK_FACE, $raw)
+		};
+	}
+
 	/**
 	 * @return int[]
 	 * @phpstan-return array<int, int>
@@ -227,22 +240,22 @@ public function readCardinalHorizontalFacing() : int{
 	public function readColor() : DyeColor{
 		//	 * color (StringTag) = black, blue, brown, cyan, gray, green, light_blue, lime, magenta, orange, pink, purple, red, silver, white, yellow
 		return match($color = $this->readString(BlockStateNames::COLOR)){
-			StringValues::COLOR_BLACK => DyeColor::BLACK(),
-			StringValues::COLOR_BLUE => DyeColor::BLUE(),
-			StringValues::COLOR_BROWN => DyeColor::BROWN(),
-			StringValues::COLOR_CYAN => DyeColor::CYAN(),
-			StringValues::COLOR_GRAY => DyeColor::GRAY(),
-			StringValues::COLOR_GREEN => DyeColor::GREEN(),
-			StringValues::COLOR_LIGHT_BLUE => DyeColor::LIGHT_BLUE(),
-			StringValues::COLOR_LIME => DyeColor::LIME(),
-			StringValues::COLOR_MAGENTA => DyeColor::MAGENTA(),
-			StringValues::COLOR_ORANGE => DyeColor::ORANGE(),
-			StringValues::COLOR_PINK => DyeColor::PINK(),
-			StringValues::COLOR_PURPLE => DyeColor::PURPLE(),
-			StringValues::COLOR_RED => DyeColor::RED(),
-			StringValues::COLOR_SILVER => DyeColor::LIGHT_GRAY(),
-			StringValues::COLOR_WHITE => DyeColor::WHITE(),
-			StringValues::COLOR_YELLOW => DyeColor::YELLOW(),
+			StringValues::COLOR_BLACK => DyeColor::BLACK,
+			StringValues::COLOR_BLUE => DyeColor::BLUE,
+			StringValues::COLOR_BROWN => DyeColor::BROWN,
+			StringValues::COLOR_CYAN => DyeColor::CYAN,
+			StringValues::COLOR_GRAY => DyeColor::GRAY,
+			StringValues::COLOR_GREEN => DyeColor::GREEN,
+			StringValues::COLOR_LIGHT_BLUE => DyeColor::LIGHT_BLUE,
+			StringValues::COLOR_LIME => DyeColor::LIME,
+			StringValues::COLOR_MAGENTA => DyeColor::MAGENTA,
+			StringValues::COLOR_ORANGE => DyeColor::ORANGE,
+			StringValues::COLOR_PINK => DyeColor::PINK,
+			StringValues::COLOR_PURPLE => DyeColor::PURPLE,
+			StringValues::COLOR_RED => DyeColor::RED,
+			StringValues::COLOR_SILVER => DyeColor::LIGHT_GRAY,
+			StringValues::COLOR_WHITE => DyeColor::WHITE,
+			StringValues::COLOR_YELLOW => DyeColor::YELLOW,
 			default => throw $this->badValueException(BlockStateNames::COLOR, $color),
 		};
 	}
@@ -294,8 +307,8 @@ public function readPillarAxis() : int{
 	/** @throws BlockStateDeserializeException */
 	public function readSlabPosition() : SlabType{
 		return match($rawValue = $this->readString(BlockStateNames::MC_VERTICAL_HALF)){
-			StringValues::MC_VERTICAL_HALF_BOTTOM => SlabType::BOTTOM(),
-			StringValues::MC_VERTICAL_HALF_TOP => SlabType::TOP(),
+			StringValues::MC_VERTICAL_HALF_BOTTOM => SlabType::BOTTOM,
+			StringValues::MC_VERTICAL_HALF_TOP => SlabType::TOP,
 			default => throw $this->badValueException(BlockStateNames::MC_VERTICAL_HALF, $rawValue, "Invalid slab position"),
 		};
 	}
@@ -320,11 +333,11 @@ public function readTorchFacing() : int{
 	/** @throws BlockStateDeserializeException */
 	public function readCoralType() : CoralType{
 		return match($type = $this->readString(BlockStateNames::CORAL_COLOR)){
-			StringValues::CORAL_COLOR_BLUE => CoralType::TUBE(),
-			StringValues::CORAL_COLOR_PINK => CoralType::BRAIN(),
-			StringValues::CORAL_COLOR_PURPLE => CoralType::BUBBLE(),
-			StringValues::CORAL_COLOR_RED => CoralType::FIRE(),
-			StringValues::CORAL_COLOR_YELLOW => CoralType::HORN(),
+			StringValues::CORAL_COLOR_BLUE => CoralType::TUBE,
+			StringValues::CORAL_COLOR_PINK => CoralType::BRAIN,
+			StringValues::CORAL_COLOR_PURPLE => CoralType::BUBBLE,
+			StringValues::CORAL_COLOR_RED => CoralType::FIRE,
+			StringValues::CORAL_COLOR_YELLOW => CoralType::HORN,
 			default => throw $this->badValueException(BlockStateNames::CORAL_COLOR, $type),
 		};
 	}
@@ -332,10 +345,10 @@ public function readCoralType() : CoralType{
 	/** @throws BlockStateDeserializeException */
 	public function readBellAttachmentType() : BellAttachmentType{
 		return match($type = $this->readString(BlockStateNames::ATTACHMENT)){
-			StringValues::ATTACHMENT_HANGING => BellAttachmentType::CEILING(),
-			StringValues::ATTACHMENT_STANDING => BellAttachmentType::FLOOR(),
-			StringValues::ATTACHMENT_SIDE => BellAttachmentType::ONE_WALL(),
-			StringValues::ATTACHMENT_MULTIPLE => BellAttachmentType::TWO_WALLS(),
+			StringValues::ATTACHMENT_HANGING => BellAttachmentType::CEILING,
+			StringValues::ATTACHMENT_STANDING => BellAttachmentType::FLOOR,
+			StringValues::ATTACHMENT_SIDE => BellAttachmentType::ONE_WALL,
+			StringValues::ATTACHMENT_MULTIPLE => BellAttachmentType::TWO_WALLS,
 			default => throw $this->badValueException(BlockStateNames::ATTACHMENT, $type),
 		};
 	}
@@ -347,8 +360,8 @@ public function readWallConnectionType(string $name) : ?WallConnectionType{
 			//we need to find a better way to auto-generate the constant names when they are reused
 			//for now, using these constants is better than nothing since it still gives static analysability
 			StringValues::WALL_CONNECTION_TYPE_EAST_NONE => null,
-			StringValues::WALL_CONNECTION_TYPE_EAST_SHORT => WallConnectionType::SHORT(),
-			StringValues::WALL_CONNECTION_TYPE_EAST_TALL => WallConnectionType::TALL(),
+			StringValues::WALL_CONNECTION_TYPE_EAST_SHORT => WallConnectionType::SHORT,
+			StringValues::WALL_CONNECTION_TYPE_EAST_TALL => WallConnectionType::TALL,
 			default => throw $this->badValueException($name, $type),
 		};
 	}
diff --git a/src/data/bedrock/block/convert/BlockStateSerializerHelper.php b/src/data/bedrock/block/convert/BlockStateSerializerHelper.php
index bd9c2937d64..2822324e5c5 100644
--- a/src/data/bedrock/block/convert/BlockStateSerializerHelper.php
+++ b/src/data/bedrock/block/convert/BlockStateSerializerHelper.php
@@ -54,7 +54,6 @@
 use pocketmine\data\bedrock\block\convert\BlockStateWriter as Writer;
 use pocketmine\data\bedrock\MushroomBlockTypeIdMap;
 use pocketmine\math\Facing;
-use pocketmine\utils\AssumptionFailedError;
 
 final class BlockStateSerializerHelper{
 
@@ -101,11 +100,10 @@ public static function encodeCauldron(string $liquid, int $fillLevel) : BlockSta
 
 	public static function selectCopperId(CopperOxidation $oxidation, string $noneId, string $exposedId, string $weatheredId, string $oxidizedId) : string{
 		return match($oxidation){
-			CopperOxidation::NONE() => $noneId,
-			CopperOxidation::EXPOSED() => $exposedId,
-			CopperOxidation::WEATHERED() => $weatheredId,
-			CopperOxidation::OXIDIZED() => $oxidizedId,
-			default => throw new AssumptionFailedError("Unhandled copper oxidation " . $oxidation->name())
+			CopperOxidation::NONE => $noneId,
+			CopperOxidation::EXPOSED => $exposedId,
+			CopperOxidation::WEATHERED => $weatheredId,
+			CopperOxidation::OXIDIZED => $oxidizedId,
 		};
 	}
 
@@ -210,9 +208,9 @@ public static function encodeSimplePressurePlate(SimplePressurePlate $block, Blo
 
 	public static function encodeSlab(Slab $block, string $singleId, string $doubleId) : BlockStateWriter{
 		$slabType = $block->getSlabType();
-		return BlockStateWriter::create($slabType->equals(SlabType::DOUBLE()) ? $doubleId : $singleId)
+		return BlockStateWriter::create($slabType === SlabType::DOUBLE ? $doubleId : $singleId)
 			//this is (intentionally) also written for double slabs (as zero) to maintain bug parity with MCPE
-			->writeSlabPosition($slabType->equals(SlabType::DOUBLE()) ? SlabType::BOTTOM() : $slabType);
+			->writeSlabPosition($slabType === SlabType::DOUBLE ? SlabType::BOTTOM : $slabType);
 	}
 
 	public static function encodeStairs(Stair $block, BlockStateWriter $out) : BlockStateWriter{
diff --git a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php
index ce39c2739c5..df19250d031 100644
--- a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php
+++ b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php
@@ -23,16 +23,20 @@
 
 namespace pocketmine\data\bedrock\block\convert;
 
+use pocketmine\block\AmethystCluster;
 use pocketmine\block\Bamboo;
 use pocketmine\block\Block;
 use pocketmine\block\CaveVines;
 use pocketmine\block\ChorusFlower;
+use pocketmine\block\DoublePitcherCrop;
 use pocketmine\block\Light;
 use pocketmine\block\PinkPetals;
+use pocketmine\block\PitcherCrop;
 use pocketmine\block\Slab;
 use pocketmine\block\Stair;
 use pocketmine\block\SweetBerryBush;
 use pocketmine\block\utils\BrewingStandSlot;
+use pocketmine\block\utils\ChiseledBookshelfSlot;
 use pocketmine\block\utils\CopperOxidation;
 use pocketmine\block\utils\CoralType;
 use pocketmine\block\utils\DirtType;
@@ -114,7 +118,7 @@ public function mapSlab(string $singleId, string $doubleId, \Closure $getBlock)
 		$this->map($singleId, fn(Reader $in) : Slab => $getBlock($in)->setSlabType($in->readSlabPosition()));
 		$this->map($doubleId, function(Reader $in) use ($getBlock) : Slab{
 			$in->ignored(StateNames::MC_VERTICAL_HALF);
-			return $getBlock($in)->setSlabType(SlabType::DOUBLE());
+			return $getBlock($in)->setSlabType(SlabType::DOUBLE);
 		});
 	}
 
@@ -134,44 +138,44 @@ public function mapLog(string $unstrippedId, string $strippedId, \Closure $getBl
 	private function registerCandleDeserializers() : void{
 		$this->map(Ids::CANDLE, fn(Reader $in) => Helper::decodeCandle(Blocks::CANDLE(), $in));
 		foreach([
-			Ids::BLACK_CANDLE => DyeColor::BLACK(),
-			Ids::BLUE_CANDLE => DyeColor::BLUE(),
-			Ids::BROWN_CANDLE => DyeColor::BROWN(),
-			Ids::CYAN_CANDLE => DyeColor::CYAN(),
-			Ids::GRAY_CANDLE => DyeColor::GRAY(),
-			Ids::GREEN_CANDLE => DyeColor::GREEN(),
-			Ids::LIGHT_BLUE_CANDLE => DyeColor::LIGHT_BLUE(),
-			Ids::LIGHT_GRAY_CANDLE => DyeColor::LIGHT_GRAY(),
-			Ids::LIME_CANDLE => DyeColor::LIME(),
-			Ids::MAGENTA_CANDLE => DyeColor::MAGENTA(),
-			Ids::ORANGE_CANDLE => DyeColor::ORANGE(),
-			Ids::PINK_CANDLE => DyeColor::PINK(),
-			Ids::PURPLE_CANDLE => DyeColor::PURPLE(),
-			Ids::RED_CANDLE => DyeColor::RED(),
-			Ids::WHITE_CANDLE => DyeColor::WHITE(),
-			Ids::YELLOW_CANDLE => DyeColor::YELLOW(),
+			Ids::BLACK_CANDLE => DyeColor::BLACK,
+			Ids::BLUE_CANDLE => DyeColor::BLUE,
+			Ids::BROWN_CANDLE => DyeColor::BROWN,
+			Ids::CYAN_CANDLE => DyeColor::CYAN,
+			Ids::GRAY_CANDLE => DyeColor::GRAY,
+			Ids::GREEN_CANDLE => DyeColor::GREEN,
+			Ids::LIGHT_BLUE_CANDLE => DyeColor::LIGHT_BLUE,
+			Ids::LIGHT_GRAY_CANDLE => DyeColor::LIGHT_GRAY,
+			Ids::LIME_CANDLE => DyeColor::LIME,
+			Ids::MAGENTA_CANDLE => DyeColor::MAGENTA,
+			Ids::ORANGE_CANDLE => DyeColor::ORANGE,
+			Ids::PINK_CANDLE => DyeColor::PINK,
+			Ids::PURPLE_CANDLE => DyeColor::PURPLE,
+			Ids::RED_CANDLE => DyeColor::RED,
+			Ids::WHITE_CANDLE => DyeColor::WHITE,
+			Ids::YELLOW_CANDLE => DyeColor::YELLOW,
 		] as $id => $color){
 			$this->map($id, fn(Reader $in) => Helper::decodeCandle(Blocks::DYED_CANDLE()->setColor($color), $in));
 		}
 
 		$this->map(Ids::CANDLE_CAKE, fn(Reader $in) => Blocks::CAKE_WITH_CANDLE()->setLit($in->readBool(StateNames::LIT)));
 		foreach([
-			Ids::BLACK_CANDLE_CAKE => DyeColor::BLACK(),
-			Ids::BLUE_CANDLE_CAKE => DyeColor::BLUE(),
-			Ids::BROWN_CANDLE_CAKE => DyeColor::BROWN(),
-			Ids::CYAN_CANDLE_CAKE => DyeColor::CYAN(),
-			Ids::GRAY_CANDLE_CAKE => DyeColor::GRAY(),
-			Ids::GREEN_CANDLE_CAKE => DyeColor::GREEN(),
-			Ids::LIGHT_BLUE_CANDLE_CAKE => DyeColor::LIGHT_BLUE(),
-			Ids::LIGHT_GRAY_CANDLE_CAKE => DyeColor::LIGHT_GRAY(),
-			Ids::LIME_CANDLE_CAKE => DyeColor::LIME(),
-			Ids::MAGENTA_CANDLE_CAKE => DyeColor::MAGENTA(),
-			Ids::ORANGE_CANDLE_CAKE => DyeColor::ORANGE(),
-			Ids::PINK_CANDLE_CAKE => DyeColor::PINK(),
-			Ids::PURPLE_CANDLE_CAKE => DyeColor::PURPLE(),
-			Ids::RED_CANDLE_CAKE => DyeColor::RED(),
-			Ids::WHITE_CANDLE_CAKE => DyeColor::WHITE(),
-			Ids::YELLOW_CANDLE_CAKE => DyeColor::YELLOW(),
+			Ids::BLACK_CANDLE_CAKE => DyeColor::BLACK,
+			Ids::BLUE_CANDLE_CAKE => DyeColor::BLUE,
+			Ids::BROWN_CANDLE_CAKE => DyeColor::BROWN,
+			Ids::CYAN_CANDLE_CAKE => DyeColor::CYAN,
+			Ids::GRAY_CANDLE_CAKE => DyeColor::GRAY,
+			Ids::GREEN_CANDLE_CAKE => DyeColor::GREEN,
+			Ids::LIGHT_BLUE_CANDLE_CAKE => DyeColor::LIGHT_BLUE,
+			Ids::LIGHT_GRAY_CANDLE_CAKE => DyeColor::LIGHT_GRAY,
+			Ids::LIME_CANDLE_CAKE => DyeColor::LIME,
+			Ids::MAGENTA_CANDLE_CAKE => DyeColor::MAGENTA,
+			Ids::ORANGE_CANDLE_CAKE => DyeColor::ORANGE,
+			Ids::PINK_CANDLE_CAKE => DyeColor::PINK,
+			Ids::PURPLE_CANDLE_CAKE => DyeColor::PURPLE,
+			Ids::RED_CANDLE_CAKE => DyeColor::RED,
+			Ids::WHITE_CANDLE_CAKE => DyeColor::WHITE,
+			Ids::YELLOW_CANDLE_CAKE => DyeColor::YELLOW,
 		] as $id => $color){
 			$this->map($id, fn(Reader $in) => Blocks::CAKE_WITH_DYED_CANDLE()
 				->setColor($color)
@@ -182,22 +186,22 @@ private function registerCandleDeserializers() : void{
 
 	private function registerFlatColorBlockDeserializers() : void{
 		foreach([
-			Ids::BLACK_GLAZED_TERRACOTTA => DyeColor::BLACK(),
-			Ids::BLUE_GLAZED_TERRACOTTA => DyeColor::BLUE(),
-			Ids::BROWN_GLAZED_TERRACOTTA => DyeColor::BROWN(),
-			Ids::CYAN_GLAZED_TERRACOTTA => DyeColor::CYAN(),
-			Ids::GRAY_GLAZED_TERRACOTTA => DyeColor::GRAY(),
-			Ids::GREEN_GLAZED_TERRACOTTA => DyeColor::GREEN(),
-			Ids::LIGHT_BLUE_GLAZED_TERRACOTTA => DyeColor::LIGHT_BLUE(),
-			Ids::SILVER_GLAZED_TERRACOTTA => DyeColor::LIGHT_GRAY(),
-			Ids::LIME_GLAZED_TERRACOTTA => DyeColor::LIME(),
-			Ids::MAGENTA_GLAZED_TERRACOTTA => DyeColor::MAGENTA(),
-			Ids::ORANGE_GLAZED_TERRACOTTA => DyeColor::ORANGE(),
-			Ids::PINK_GLAZED_TERRACOTTA => DyeColor::PINK(),
-			Ids::PURPLE_GLAZED_TERRACOTTA => DyeColor::PURPLE(),
-			Ids::RED_GLAZED_TERRACOTTA => DyeColor::RED(),
-			Ids::WHITE_GLAZED_TERRACOTTA => DyeColor::WHITE(),
-			Ids::YELLOW_GLAZED_TERRACOTTA => DyeColor::YELLOW(),
+			Ids::BLACK_GLAZED_TERRACOTTA => DyeColor::BLACK,
+			Ids::BLUE_GLAZED_TERRACOTTA => DyeColor::BLUE,
+			Ids::BROWN_GLAZED_TERRACOTTA => DyeColor::BROWN,
+			Ids::CYAN_GLAZED_TERRACOTTA => DyeColor::CYAN,
+			Ids::GRAY_GLAZED_TERRACOTTA => DyeColor::GRAY,
+			Ids::GREEN_GLAZED_TERRACOTTA => DyeColor::GREEN,
+			Ids::LIGHT_BLUE_GLAZED_TERRACOTTA => DyeColor::LIGHT_BLUE,
+			Ids::SILVER_GLAZED_TERRACOTTA => DyeColor::LIGHT_GRAY,
+			Ids::LIME_GLAZED_TERRACOTTA => DyeColor::LIME,
+			Ids::MAGENTA_GLAZED_TERRACOTTA => DyeColor::MAGENTA,
+			Ids::ORANGE_GLAZED_TERRACOTTA => DyeColor::ORANGE,
+			Ids::PINK_GLAZED_TERRACOTTA => DyeColor::PINK,
+			Ids::PURPLE_GLAZED_TERRACOTTA => DyeColor::PURPLE,
+			Ids::RED_GLAZED_TERRACOTTA => DyeColor::RED,
+			Ids::WHITE_GLAZED_TERRACOTTA => DyeColor::WHITE,
+			Ids::YELLOW_GLAZED_TERRACOTTA => DyeColor::YELLOW,
 		] as $id => $color){
 			$this->map($id, fn(Reader $in) => Blocks::GLAZED_TERRACOTTA()
 				->setColor($color)
@@ -206,169 +210,169 @@ private function registerFlatColorBlockDeserializers() : void{
 		}
 
 		foreach([
-			Ids::BLACK_WOOL => DyeColor::BLACK(),
-			Ids::BLUE_WOOL => DyeColor::BLUE(),
-			Ids::BROWN_WOOL => DyeColor::BROWN(),
-			Ids::CYAN_WOOL => DyeColor::CYAN(),
-			Ids::GRAY_WOOL => DyeColor::GRAY(),
-			Ids::GREEN_WOOL => DyeColor::GREEN(),
-			Ids::LIGHT_BLUE_WOOL => DyeColor::LIGHT_BLUE(),
-			Ids::LIGHT_GRAY_WOOL => DyeColor::LIGHT_GRAY(),
-			Ids::LIME_WOOL => DyeColor::LIME(),
-			Ids::MAGENTA_WOOL => DyeColor::MAGENTA(),
-			Ids::ORANGE_WOOL => DyeColor::ORANGE(),
-			Ids::PINK_WOOL => DyeColor::PINK(),
-			Ids::PURPLE_WOOL => DyeColor::PURPLE(),
-			Ids::RED_WOOL => DyeColor::RED(),
-			Ids::WHITE_WOOL => DyeColor::WHITE(),
-			Ids::YELLOW_WOOL => DyeColor::YELLOW(),
+			Ids::BLACK_WOOL => DyeColor::BLACK,
+			Ids::BLUE_WOOL => DyeColor::BLUE,
+			Ids::BROWN_WOOL => DyeColor::BROWN,
+			Ids::CYAN_WOOL => DyeColor::CYAN,
+			Ids::GRAY_WOOL => DyeColor::GRAY,
+			Ids::GREEN_WOOL => DyeColor::GREEN,
+			Ids::LIGHT_BLUE_WOOL => DyeColor::LIGHT_BLUE,
+			Ids::LIGHT_GRAY_WOOL => DyeColor::LIGHT_GRAY,
+			Ids::LIME_WOOL => DyeColor::LIME,
+			Ids::MAGENTA_WOOL => DyeColor::MAGENTA,
+			Ids::ORANGE_WOOL => DyeColor::ORANGE,
+			Ids::PINK_WOOL => DyeColor::PINK,
+			Ids::PURPLE_WOOL => DyeColor::PURPLE,
+			Ids::RED_WOOL => DyeColor::RED,
+			Ids::WHITE_WOOL => DyeColor::WHITE,
+			Ids::YELLOW_WOOL => DyeColor::YELLOW,
 		] as $id => $color){
 			$this->mapSimple($id, fn() => Blocks::WOOL()->setColor($color));
 		}
 
 		foreach([
-			Ids::BLACK_CARPET => DyeColor::BLACK(),
-			Ids::BLUE_CARPET => DyeColor::BLUE(),
-			Ids::BROWN_CARPET => DyeColor::BROWN(),
-			Ids::CYAN_CARPET => DyeColor::CYAN(),
-			Ids::GRAY_CARPET => DyeColor::GRAY(),
-			Ids::GREEN_CARPET => DyeColor::GREEN(),
-			Ids::LIGHT_BLUE_CARPET => DyeColor::LIGHT_BLUE(),
-			Ids::LIGHT_GRAY_CARPET => DyeColor::LIGHT_GRAY(),
-			Ids::LIME_CARPET => DyeColor::LIME(),
-			Ids::MAGENTA_CARPET => DyeColor::MAGENTA(),
-			Ids::ORANGE_CARPET => DyeColor::ORANGE(),
-			Ids::PINK_CARPET => DyeColor::PINK(),
-			Ids::PURPLE_CARPET => DyeColor::PURPLE(),
-			Ids::RED_CARPET => DyeColor::RED(),
-			Ids::WHITE_CARPET => DyeColor::WHITE(),
-			Ids::YELLOW_CARPET => DyeColor::YELLOW(),
+			Ids::BLACK_CARPET => DyeColor::BLACK,
+			Ids::BLUE_CARPET => DyeColor::BLUE,
+			Ids::BROWN_CARPET => DyeColor::BROWN,
+			Ids::CYAN_CARPET => DyeColor::CYAN,
+			Ids::GRAY_CARPET => DyeColor::GRAY,
+			Ids::GREEN_CARPET => DyeColor::GREEN,
+			Ids::LIGHT_BLUE_CARPET => DyeColor::LIGHT_BLUE,
+			Ids::LIGHT_GRAY_CARPET => DyeColor::LIGHT_GRAY,
+			Ids::LIME_CARPET => DyeColor::LIME,
+			Ids::MAGENTA_CARPET => DyeColor::MAGENTA,
+			Ids::ORANGE_CARPET => DyeColor::ORANGE,
+			Ids::PINK_CARPET => DyeColor::PINK,
+			Ids::PURPLE_CARPET => DyeColor::PURPLE,
+			Ids::RED_CARPET => DyeColor::RED,
+			Ids::WHITE_CARPET => DyeColor::WHITE,
+			Ids::YELLOW_CARPET => DyeColor::YELLOW,
 		] as $id => $color){
 			$this->mapSimple($id, fn() => Blocks::CARPET()->setColor($color));
 		}
 
 		foreach([
-			Ids::BLACK_SHULKER_BOX => DyeColor::BLACK(),
-			Ids::BLUE_SHULKER_BOX => DyeColor::BLUE(),
-			Ids::BROWN_SHULKER_BOX => DyeColor::BROWN(),
-			Ids::CYAN_SHULKER_BOX => DyeColor::CYAN(),
-			Ids::GRAY_SHULKER_BOX => DyeColor::GRAY(),
-			Ids::GREEN_SHULKER_BOX => DyeColor::GREEN(),
-			Ids::LIGHT_BLUE_SHULKER_BOX => DyeColor::LIGHT_BLUE(),
-			Ids::LIGHT_GRAY_SHULKER_BOX => DyeColor::LIGHT_GRAY(),
-			Ids::LIME_SHULKER_BOX => DyeColor::LIME(),
-			Ids::MAGENTA_SHULKER_BOX => DyeColor::MAGENTA(),
-			Ids::ORANGE_SHULKER_BOX => DyeColor::ORANGE(),
-			Ids::PINK_SHULKER_BOX => DyeColor::PINK(),
-			Ids::PURPLE_SHULKER_BOX => DyeColor::PURPLE(),
-			Ids::RED_SHULKER_BOX => DyeColor::RED(),
-			Ids::WHITE_SHULKER_BOX => DyeColor::WHITE(),
-			Ids::YELLOW_SHULKER_BOX => DyeColor::YELLOW(),
+			Ids::BLACK_SHULKER_BOX => DyeColor::BLACK,
+			Ids::BLUE_SHULKER_BOX => DyeColor::BLUE,
+			Ids::BROWN_SHULKER_BOX => DyeColor::BROWN,
+			Ids::CYAN_SHULKER_BOX => DyeColor::CYAN,
+			Ids::GRAY_SHULKER_BOX => DyeColor::GRAY,
+			Ids::GREEN_SHULKER_BOX => DyeColor::GREEN,
+			Ids::LIGHT_BLUE_SHULKER_BOX => DyeColor::LIGHT_BLUE,
+			Ids::LIGHT_GRAY_SHULKER_BOX => DyeColor::LIGHT_GRAY,
+			Ids::LIME_SHULKER_BOX => DyeColor::LIME,
+			Ids::MAGENTA_SHULKER_BOX => DyeColor::MAGENTA,
+			Ids::ORANGE_SHULKER_BOX => DyeColor::ORANGE,
+			Ids::PINK_SHULKER_BOX => DyeColor::PINK,
+			Ids::PURPLE_SHULKER_BOX => DyeColor::PURPLE,
+			Ids::RED_SHULKER_BOX => DyeColor::RED,
+			Ids::WHITE_SHULKER_BOX => DyeColor::WHITE,
+			Ids::YELLOW_SHULKER_BOX => DyeColor::YELLOW,
 		] as $id => $color){
 			$this->mapSimple($id, fn() => Blocks::DYED_SHULKER_BOX()->setColor($color));
 		}
 
 		foreach([
-			Ids::BLACK_CONCRETE => DyeColor::BLACK(),
-			Ids::BLUE_CONCRETE => DyeColor::BLUE(),
-			Ids::BROWN_CONCRETE => DyeColor::BROWN(),
-			Ids::CYAN_CONCRETE => DyeColor::CYAN(),
-			Ids::GRAY_CONCRETE => DyeColor::GRAY(),
-			Ids::GREEN_CONCRETE => DyeColor::GREEN(),
-			Ids::LIGHT_BLUE_CONCRETE => DyeColor::LIGHT_BLUE(),
-			Ids::LIGHT_GRAY_CONCRETE => DyeColor::LIGHT_GRAY(),
-			Ids::LIME_CONCRETE => DyeColor::LIME(),
-			Ids::MAGENTA_CONCRETE => DyeColor::MAGENTA(),
-			Ids::ORANGE_CONCRETE => DyeColor::ORANGE(),
-			Ids::PINK_CONCRETE => DyeColor::PINK(),
-			Ids::PURPLE_CONCRETE => DyeColor::PURPLE(),
-			Ids::RED_CONCRETE => DyeColor::RED(),
-			Ids::WHITE_CONCRETE => DyeColor::WHITE(),
-			Ids::YELLOW_CONCRETE => DyeColor::YELLOW(),
+			Ids::BLACK_CONCRETE => DyeColor::BLACK,
+			Ids::BLUE_CONCRETE => DyeColor::BLUE,
+			Ids::BROWN_CONCRETE => DyeColor::BROWN,
+			Ids::CYAN_CONCRETE => DyeColor::CYAN,
+			Ids::GRAY_CONCRETE => DyeColor::GRAY,
+			Ids::GREEN_CONCRETE => DyeColor::GREEN,
+			Ids::LIGHT_BLUE_CONCRETE => DyeColor::LIGHT_BLUE,
+			Ids::LIGHT_GRAY_CONCRETE => DyeColor::LIGHT_GRAY,
+			Ids::LIME_CONCRETE => DyeColor::LIME,
+			Ids::MAGENTA_CONCRETE => DyeColor::MAGENTA,
+			Ids::ORANGE_CONCRETE => DyeColor::ORANGE,
+			Ids::PINK_CONCRETE => DyeColor::PINK,
+			Ids::PURPLE_CONCRETE => DyeColor::PURPLE,
+			Ids::RED_CONCRETE => DyeColor::RED,
+			Ids::WHITE_CONCRETE => DyeColor::WHITE,
+			Ids::YELLOW_CONCRETE => DyeColor::YELLOW,
 		] as $id => $color){
 			$this->mapSimple($id, fn() => Blocks::CONCRETE()->setColor($color));
 		}
 
 		foreach([
-			Ids::BLACK_CONCRETE_POWDER => DyeColor::BLACK(),
-			Ids::BLUE_CONCRETE_POWDER => DyeColor::BLUE(),
-			Ids::BROWN_CONCRETE_POWDER => DyeColor::BROWN(),
-			Ids::CYAN_CONCRETE_POWDER => DyeColor::CYAN(),
-			Ids::GRAY_CONCRETE_POWDER => DyeColor::GRAY(),
-			Ids::GREEN_CONCRETE_POWDER => DyeColor::GREEN(),
-			Ids::LIGHT_BLUE_CONCRETE_POWDER => DyeColor::LIGHT_BLUE(),
-			Ids::LIGHT_GRAY_CONCRETE_POWDER => DyeColor::LIGHT_GRAY(),
-			Ids::LIME_CONCRETE_POWDER => DyeColor::LIME(),
-			Ids::MAGENTA_CONCRETE_POWDER => DyeColor::MAGENTA(),
-			Ids::ORANGE_CONCRETE_POWDER => DyeColor::ORANGE(),
-			Ids::PINK_CONCRETE_POWDER => DyeColor::PINK(),
-			Ids::PURPLE_CONCRETE_POWDER => DyeColor::PURPLE(),
-			Ids::RED_CONCRETE_POWDER => DyeColor::RED(),
-			Ids::WHITE_CONCRETE_POWDER => DyeColor::WHITE(),
-			Ids::YELLOW_CONCRETE_POWDER => DyeColor::YELLOW(),
+			Ids::BLACK_CONCRETE_POWDER => DyeColor::BLACK,
+			Ids::BLUE_CONCRETE_POWDER => DyeColor::BLUE,
+			Ids::BROWN_CONCRETE_POWDER => DyeColor::BROWN,
+			Ids::CYAN_CONCRETE_POWDER => DyeColor::CYAN,
+			Ids::GRAY_CONCRETE_POWDER => DyeColor::GRAY,
+			Ids::GREEN_CONCRETE_POWDER => DyeColor::GREEN,
+			Ids::LIGHT_BLUE_CONCRETE_POWDER => DyeColor::LIGHT_BLUE,
+			Ids::LIGHT_GRAY_CONCRETE_POWDER => DyeColor::LIGHT_GRAY,
+			Ids::LIME_CONCRETE_POWDER => DyeColor::LIME,
+			Ids::MAGENTA_CONCRETE_POWDER => DyeColor::MAGENTA,
+			Ids::ORANGE_CONCRETE_POWDER => DyeColor::ORANGE,
+			Ids::PINK_CONCRETE_POWDER => DyeColor::PINK,
+			Ids::PURPLE_CONCRETE_POWDER => DyeColor::PURPLE,
+			Ids::RED_CONCRETE_POWDER => DyeColor::RED,
+			Ids::WHITE_CONCRETE_POWDER => DyeColor::WHITE,
+			Ids::YELLOW_CONCRETE_POWDER => DyeColor::YELLOW,
 		] as $id => $color){
 			$this->mapSimple($id, fn() => Blocks::CONCRETE_POWDER()->setColor($color));
 		}
 
 		foreach([
-			Ids::BLACK_TERRACOTTA => DyeColor::BLACK(),
-			Ids::BLUE_TERRACOTTA => DyeColor::BLUE(),
-			Ids::BROWN_TERRACOTTA => DyeColor::BROWN(),
-			Ids::CYAN_TERRACOTTA => DyeColor::CYAN(),
-			Ids::GRAY_TERRACOTTA => DyeColor::GRAY(),
-			Ids::GREEN_TERRACOTTA => DyeColor::GREEN(),
-			Ids::LIGHT_BLUE_TERRACOTTA => DyeColor::LIGHT_BLUE(),
-			Ids::LIGHT_GRAY_TERRACOTTA => DyeColor::LIGHT_GRAY(),
-			Ids::LIME_TERRACOTTA => DyeColor::LIME(),
-			Ids::MAGENTA_TERRACOTTA => DyeColor::MAGENTA(),
-			Ids::ORANGE_TERRACOTTA => DyeColor::ORANGE(),
-			Ids::PINK_TERRACOTTA => DyeColor::PINK(),
-			Ids::PURPLE_TERRACOTTA => DyeColor::PURPLE(),
-			Ids::RED_TERRACOTTA => DyeColor::RED(),
-			Ids::WHITE_TERRACOTTA => DyeColor::WHITE(),
-			Ids::YELLOW_TERRACOTTA => DyeColor::YELLOW(),
+			Ids::BLACK_TERRACOTTA => DyeColor::BLACK,
+			Ids::BLUE_TERRACOTTA => DyeColor::BLUE,
+			Ids::BROWN_TERRACOTTA => DyeColor::BROWN,
+			Ids::CYAN_TERRACOTTA => DyeColor::CYAN,
+			Ids::GRAY_TERRACOTTA => DyeColor::GRAY,
+			Ids::GREEN_TERRACOTTA => DyeColor::GREEN,
+			Ids::LIGHT_BLUE_TERRACOTTA => DyeColor::LIGHT_BLUE,
+			Ids::LIGHT_GRAY_TERRACOTTA => DyeColor::LIGHT_GRAY,
+			Ids::LIME_TERRACOTTA => DyeColor::LIME,
+			Ids::MAGENTA_TERRACOTTA => DyeColor::MAGENTA,
+			Ids::ORANGE_TERRACOTTA => DyeColor::ORANGE,
+			Ids::PINK_TERRACOTTA => DyeColor::PINK,
+			Ids::PURPLE_TERRACOTTA => DyeColor::PURPLE,
+			Ids::RED_TERRACOTTA => DyeColor::RED,
+			Ids::WHITE_TERRACOTTA => DyeColor::WHITE,
+			Ids::YELLOW_TERRACOTTA => DyeColor::YELLOW,
 		] as $id => $color){
 			$this->mapSimple($id, fn() => Blocks::STAINED_CLAY()->setColor($color));
 		}
 
 		foreach([
-			Ids::BLACK_STAINED_GLASS => DyeColor::BLACK(),
-			Ids::BLUE_STAINED_GLASS => DyeColor::BLUE(),
-			Ids::BROWN_STAINED_GLASS => DyeColor::BROWN(),
-			Ids::CYAN_STAINED_GLASS => DyeColor::CYAN(),
-			Ids::GRAY_STAINED_GLASS => DyeColor::GRAY(),
-			Ids::GREEN_STAINED_GLASS => DyeColor::GREEN(),
-			Ids::LIGHT_BLUE_STAINED_GLASS => DyeColor::LIGHT_BLUE(),
-			Ids::LIGHT_GRAY_STAINED_GLASS => DyeColor::LIGHT_GRAY(),
-			Ids::LIME_STAINED_GLASS => DyeColor::LIME(),
-			Ids::MAGENTA_STAINED_GLASS => DyeColor::MAGENTA(),
-			Ids::ORANGE_STAINED_GLASS => DyeColor::ORANGE(),
-			Ids::PINK_STAINED_GLASS => DyeColor::PINK(),
-			Ids::PURPLE_STAINED_GLASS => DyeColor::PURPLE(),
-			Ids::RED_STAINED_GLASS => DyeColor::RED(),
-			Ids::WHITE_STAINED_GLASS => DyeColor::WHITE(),
-			Ids::YELLOW_STAINED_GLASS => DyeColor::YELLOW(),
+			Ids::BLACK_STAINED_GLASS => DyeColor::BLACK,
+			Ids::BLUE_STAINED_GLASS => DyeColor::BLUE,
+			Ids::BROWN_STAINED_GLASS => DyeColor::BROWN,
+			Ids::CYAN_STAINED_GLASS => DyeColor::CYAN,
+			Ids::GRAY_STAINED_GLASS => DyeColor::GRAY,
+			Ids::GREEN_STAINED_GLASS => DyeColor::GREEN,
+			Ids::LIGHT_BLUE_STAINED_GLASS => DyeColor::LIGHT_BLUE,
+			Ids::LIGHT_GRAY_STAINED_GLASS => DyeColor::LIGHT_GRAY,
+			Ids::LIME_STAINED_GLASS => DyeColor::LIME,
+			Ids::MAGENTA_STAINED_GLASS => DyeColor::MAGENTA,
+			Ids::ORANGE_STAINED_GLASS => DyeColor::ORANGE,
+			Ids::PINK_STAINED_GLASS => DyeColor::PINK,
+			Ids::PURPLE_STAINED_GLASS => DyeColor::PURPLE,
+			Ids::RED_STAINED_GLASS => DyeColor::RED,
+			Ids::WHITE_STAINED_GLASS => DyeColor::WHITE,
+			Ids::YELLOW_STAINED_GLASS => DyeColor::YELLOW,
 		] as $id => $color){
 			$this->mapSimple($id, fn() => Blocks::STAINED_GLASS()->setColor($color));
 		}
 
 		foreach([
-			Ids::BLACK_STAINED_GLASS_PANE => DyeColor::BLACK(),
-			Ids::BLUE_STAINED_GLASS_PANE => DyeColor::BLUE(),
-			Ids::BROWN_STAINED_GLASS_PANE => DyeColor::BROWN(),
-			Ids::CYAN_STAINED_GLASS_PANE => DyeColor::CYAN(),
-			Ids::GRAY_STAINED_GLASS_PANE => DyeColor::GRAY(),
-			Ids::GREEN_STAINED_GLASS_PANE => DyeColor::GREEN(),
-			Ids::LIGHT_BLUE_STAINED_GLASS_PANE => DyeColor::LIGHT_BLUE(),
-			Ids::LIGHT_GRAY_STAINED_GLASS_PANE => DyeColor::LIGHT_GRAY(),
-			Ids::LIME_STAINED_GLASS_PANE => DyeColor::LIME(),
-			Ids::MAGENTA_STAINED_GLASS_PANE => DyeColor::MAGENTA(),
-			Ids::ORANGE_STAINED_GLASS_PANE => DyeColor::ORANGE(),
-			Ids::PINK_STAINED_GLASS_PANE => DyeColor::PINK(),
-			Ids::PURPLE_STAINED_GLASS_PANE => DyeColor::PURPLE(),
-			Ids::RED_STAINED_GLASS_PANE => DyeColor::RED(),
-			Ids::WHITE_STAINED_GLASS_PANE => DyeColor::WHITE(),
-			Ids::YELLOW_STAINED_GLASS_PANE => DyeColor::YELLOW(),
+			Ids::BLACK_STAINED_GLASS_PANE => DyeColor::BLACK,
+			Ids::BLUE_STAINED_GLASS_PANE => DyeColor::BLUE,
+			Ids::BROWN_STAINED_GLASS_PANE => DyeColor::BROWN,
+			Ids::CYAN_STAINED_GLASS_PANE => DyeColor::CYAN,
+			Ids::GRAY_STAINED_GLASS_PANE => DyeColor::GRAY,
+			Ids::GREEN_STAINED_GLASS_PANE => DyeColor::GREEN,
+			Ids::LIGHT_BLUE_STAINED_GLASS_PANE => DyeColor::LIGHT_BLUE,
+			Ids::LIGHT_GRAY_STAINED_GLASS_PANE => DyeColor::LIGHT_GRAY,
+			Ids::LIME_STAINED_GLASS_PANE => DyeColor::LIME,
+			Ids::MAGENTA_STAINED_GLASS_PANE => DyeColor::MAGENTA,
+			Ids::ORANGE_STAINED_GLASS_PANE => DyeColor::ORANGE,
+			Ids::PINK_STAINED_GLASS_PANE => DyeColor::PINK,
+			Ids::PURPLE_STAINED_GLASS_PANE => DyeColor::PURPLE,
+			Ids::RED_STAINED_GLASS_PANE => DyeColor::RED,
+			Ids::WHITE_STAINED_GLASS_PANE => DyeColor::WHITE,
+			Ids::YELLOW_STAINED_GLASS_PANE => DyeColor::YELLOW,
 		] as $id => $color){
 			$this->mapSimple($id, fn() => Blocks::STAINED_GLASS_PANE()->setColor($color));
 		}
@@ -376,20 +380,20 @@ private function registerFlatColorBlockDeserializers() : void{
 
 	private function registerFlatCoralDeserializers() : void{
 		foreach([
-			Ids::BRAIN_CORAL => CoralType::BRAIN(),
-			Ids::BUBBLE_CORAL => CoralType::BUBBLE(),
-			Ids::FIRE_CORAL => CoralType::FIRE(),
-			Ids::HORN_CORAL => CoralType::HORN(),
-			Ids::TUBE_CORAL => CoralType::TUBE(),
+			Ids::BRAIN_CORAL => CoralType::BRAIN,
+			Ids::BUBBLE_CORAL => CoralType::BUBBLE,
+			Ids::FIRE_CORAL => CoralType::FIRE,
+			Ids::HORN_CORAL => CoralType::HORN,
+			Ids::TUBE_CORAL => CoralType::TUBE,
 		] as $id => $coralType){
 			$this->mapSimple($id, fn() => Blocks::CORAL()->setCoralType($coralType)->setDead(false));
 		}
 		foreach([
-			Ids::DEAD_BRAIN_CORAL => CoralType::BRAIN(),
-			Ids::DEAD_BUBBLE_CORAL => CoralType::BUBBLE(),
-			Ids::DEAD_FIRE_CORAL => CoralType::FIRE(),
-			Ids::DEAD_HORN_CORAL => CoralType::HORN(),
-			Ids::DEAD_TUBE_CORAL => CoralType::TUBE(),
+			Ids::DEAD_BRAIN_CORAL => CoralType::BRAIN,
+			Ids::DEAD_BUBBLE_CORAL => CoralType::BUBBLE,
+			Ids::DEAD_FIRE_CORAL => CoralType::FIRE,
+			Ids::DEAD_HORN_CORAL => CoralType::HORN,
+			Ids::DEAD_TUBE_CORAL => CoralType::TUBE,
 		] as $id => $coralType){
 			$this->mapSimple($id, fn() => Blocks::CORAL()->setCoralType($coralType)->setDead(true));
 		}
@@ -611,6 +615,7 @@ private function registerSimpleDeserializers() : void{
 		$this->mapSimple(Ids::BOOKSHELF, fn() => Blocks::BOOKSHELF());
 		$this->mapSimple(Ids::BRICK_BLOCK, fn() => Blocks::BRICKS());
 		$this->mapSimple(Ids::BROWN_MUSHROOM, fn() => Blocks::BROWN_MUSHROOM());
+		$this->mapSimple(Ids::BUDDING_AMETHYST, fn() => Blocks::BUDDING_AMETHYST());
 		$this->mapSimple(Ids::CALCITE, fn() => Blocks::CALCITE());
 		$this->mapSimple(Ids::CARTOGRAPHY_TABLE, fn() => Blocks::CARTOGRAPHY_TABLE());
 		$this->mapSimple(Ids::CHEMICAL_HEAT, fn() => Blocks::CHEMICAL_HEAT());
@@ -629,6 +634,7 @@ private function registerSimpleDeserializers() : void{
 		$this->mapSimple(Ids::CRACKED_NETHER_BRICKS, fn() => Blocks::CRACKED_NETHER_BRICKS());
 		$this->mapSimple(Ids::CRACKED_POLISHED_BLACKSTONE_BRICKS, fn() => Blocks::CRACKED_POLISHED_BLACKSTONE_BRICKS());
 		$this->mapSimple(Ids::CRAFTING_TABLE, fn() => Blocks::CRAFTING_TABLE());
+		$this->mapSimple(Ids::CRIMSON_ROOTS, fn() => Blocks::CRIMSON_ROOTS());
 		$this->mapSimple(Ids::CRYING_OBSIDIAN, fn() => Blocks::CRYING_OBSIDIAN());
 		$this->mapSimple(Ids::DEADBUSH, fn() => Blocks::DEAD_BUSH());
 		$this->mapSimple(Ids::DEEPSLATE_BRICKS, fn() => Blocks::DEEPSLATE_BRICKS());
@@ -840,9 +846,11 @@ private function registerSimpleDeserializers() : void{
 		$this->mapSimple(Ids::SPORE_BLOSSOM, fn() => Blocks::SPORE_BLOSSOM());
 		$this->mapSimple(Ids::STONECUTTER, fn() => Blocks::LEGACY_STONECUTTER());
 		$this->mapSimple(Ids::TINTED_GLASS, fn() => Blocks::TINTED_GLASS());
+		$this->mapSimple(Ids::TORCHFLOWER, fn() => Blocks::TORCHFLOWER());
 		$this->mapSimple(Ids::TUFF, fn() => Blocks::TUFF());
 		$this->mapSimple(Ids::UNDYED_SHULKER_BOX, fn() => Blocks::SHULKER_BOX());
 		$this->mapSimple(Ids::WARPED_WART_BLOCK, fn() => Blocks::WARPED_WART_BLOCK());
+		$this->mapSimple(Ids::WARPED_ROOTS, fn() => Blocks::WARPED_ROOTS());
 		$this->mapSimple(Ids::WATERLILY, fn() => Blocks::LILY_PAD());
 		$this->mapSimple(Ids::WEB, fn() => Blocks::COBWEB());
 		$this->mapSimple(Ids::WITHER_ROSE, fn() => Blocks::WITHER_ROSE());
@@ -855,6 +863,11 @@ private function registerDeserializers() : void{
 				->setPowered($in->readBool(StateNames::RAIL_DATA_BIT))
 				->setShape($in->readBoundedInt(StateNames::RAIL_DIRECTION, 0, 5));
 		});
+		$this->map(Ids::AMETHYST_CLUSTER, function(Reader $in) : Block{
+			return Blocks::AMETHYST_CLUSTER()
+				->setStage(AmethystCluster::STAGE_CLUSTER)
+				->setFacing($in->readBlockFace());
+		});
 		$this->mapStairs(Ids::ANDESITE_STAIRS, fn() => Blocks::ANDESITE_STAIRS());
 		$this->map(Ids::ANVIL, function(Reader $in) : Block{
 			return Blocks::ANVIL()
@@ -917,10 +930,10 @@ private function registerDeserializers() : void{
 				return Blocks::BIG_DRIPLEAF_HEAD()
 					->setFacing($in->readCardinalHorizontalFacing())
 					->setLeafState(match($type = $in->readString(StateNames::BIG_DRIPLEAF_TILT)){
-						StringValues::BIG_DRIPLEAF_TILT_NONE => DripleafState::STABLE(),
-						StringValues::BIG_DRIPLEAF_TILT_UNSTABLE => DripleafState::UNSTABLE(),
-						StringValues::BIG_DRIPLEAF_TILT_PARTIAL_TILT => DripleafState::PARTIAL_TILT(),
-						StringValues::BIG_DRIPLEAF_TILT_FULL_TILT => DripleafState::FULL_TILT(),
+						StringValues::BIG_DRIPLEAF_TILT_NONE => DripleafState::STABLE,
+						StringValues::BIG_DRIPLEAF_TILT_UNSTABLE => DripleafState::UNSTABLE,
+						StringValues::BIG_DRIPLEAF_TILT_PARTIAL_TILT => DripleafState::PARTIAL_TILT,
+						StringValues::BIG_DRIPLEAF_TILT_FULL_TILT => DripleafState::FULL_TILT,
 						default => throw $in->badValueException(StateNames::BIG_DRIPLEAF_TILT, $type),
 					});
 			}else{
@@ -942,9 +955,9 @@ private function registerDeserializers() : void{
 		});
 		$this->map(Ids::BREWING_STAND, function(Reader $in) : Block{
 			return Blocks::BREWING_STAND()
-				->setSlot(BrewingStandSlot::EAST(), $in->readBool(StateNames::BREWING_STAND_SLOT_A_BIT))
-				->setSlot(BrewingStandSlot::SOUTHWEST(), $in->readBool(StateNames::BREWING_STAND_SLOT_B_BIT))
-				->setSlot(BrewingStandSlot::NORTHWEST(), $in->readBool(StateNames::BREWING_STAND_SLOT_C_BIT));
+				->setSlot(BrewingStandSlot::EAST, $in->readBool(StateNames::BREWING_STAND_SLOT_A_BIT))
+				->setSlot(BrewingStandSlot::SOUTHWEST, $in->readBool(StateNames::BREWING_STAND_SLOT_B_BIT))
+				->setSlot(BrewingStandSlot::NORTHWEST, $in->readBool(StateNames::BREWING_STAND_SLOT_C_BIT));
 		});
 		$this->mapStairs(Ids::BRICK_STAIRS, fn() => Blocks::BRICK_STAIRS());
 		$this->map(Ids::BROWN_MUSHROOM_BLOCK, fn(Reader $in) => Helper::decodeMushroomBlock(Blocks::BROWN_MUSHROOM_BLOCK(), $in));
@@ -983,6 +996,18 @@ private function registerDeserializers() : void{
 			return Blocks::CHAIN()
 				->setAxis($in->readPillarAxis());
 		});
+		$this->map(Ids::CHISELED_BOOKSHELF, function(Reader $in) : Block{
+			$block = Blocks::CHISELED_BOOKSHELF()
+				->setFacing($in->readLegacyHorizontalFacing());
+
+			//we don't use API constant for bounds here as the data bounds might be different to what we support internally
+			$flags = $in->readBoundedInt(StateNames::BOOKS_STORED, 0, (1 << 6) - 1);
+			foreach(ChiseledBookshelfSlot::cases() as $slot){
+				$block->setSlot($slot, ($flags & (1 << $slot->value)) !== 0);
+			}
+
+			return $block;
+		});
 		$this->map(Ids::CHEMISTRY_TABLE, function(Reader $in) : Block{
 			return (match($type = $in->readString(StateNames::CHEMISTRY_TABLE_TYPE)){
 				StringValues::CHEMISTRY_TABLE_TYPE_COMPOUND_CREATOR => Blocks::COMPOUND_CREATOR(),
@@ -1019,10 +1044,10 @@ private function registerDeserializers() : void{
 				Blocks::GREEN_TORCH()->setFacing($in->readTorchFacing()) :
 				Blocks::RED_TORCH()->setFacing($in->readTorchFacing());
 		});
-		$this->map(Ids::COPPER_BLOCK, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::NONE()));
-		$this->map(Ids::CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::NONE()));
-		$this->mapSlab(Ids::CUT_COPPER_SLAB, Ids::DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::NONE()));
-		$this->mapStairs(Ids::CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::NONE()));
+		$this->map(Ids::COPPER_BLOCK, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::NONE));
+		$this->map(Ids::CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::NONE));
+		$this->mapSlab(Ids::CUT_COPPER_SLAB, Ids::DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::NONE));
+		$this->mapStairs(Ids::CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::NONE));
 		$this->map(Ids::CORAL_BLOCK, function(Reader $in) : Block{
 			return Blocks::CORAL_BLOCK()
 				->setCoralType($in->readCoralType())
@@ -1033,13 +1058,13 @@ private function registerDeserializers() : void{
 		$this->map(Ids::CORAL_FAN_DEAD, fn(Reader $in) => Helper::decodeFloorCoralFan(Blocks::CORAL_FAN(), $in)
 				->setDead(true));
 		$this->map(Ids::CORAL_FAN_HANG, fn(Reader $in) => Helper::decodeWallCoralFan(Blocks::WALL_CORAL_FAN(), $in)
-				->setCoralType($in->readBool(StateNames::CORAL_HANG_TYPE_BIT) ? CoralType::BRAIN() : CoralType::TUBE()));
+				->setCoralType($in->readBool(StateNames::CORAL_HANG_TYPE_BIT) ? CoralType::BRAIN : CoralType::TUBE));
 		$this->map(Ids::CORAL_FAN_HANG2, fn(Reader $in) => Helper::decodeWallCoralFan(Blocks::WALL_CORAL_FAN(), $in)
-				->setCoralType($in->readBool(StateNames::CORAL_HANG_TYPE_BIT) ? CoralType::FIRE() : CoralType::BUBBLE()));
+				->setCoralType($in->readBool(StateNames::CORAL_HANG_TYPE_BIT) ? CoralType::FIRE : CoralType::BUBBLE));
 		$this->map(Ids::CORAL_FAN_HANG3, function(Reader $in) : Block{
 			$in->ignored(StateNames::CORAL_HANG_TYPE_BIT); //the game always writes this, even though it's not used
 			return Helper::decodeWallCoralFan(Blocks::WALL_CORAL_FAN(), $in)
-				->setCoralType(CoralType::HORN());
+				->setCoralType(CoralType::HORN);
 		});
 		$this->mapStairs(Ids::DARK_PRISMARINE_STAIRS, fn() => Blocks::DARK_PRISMARINE_STAIRS());
 		$this->map(Ids::DAYLIGHT_DETECTOR, fn(Reader $in) => Helper::decodeDaylightSensor(Blocks::DAYLIGHT_SENSOR(), $in)
@@ -1066,12 +1091,12 @@ private function registerDeserializers() : void{
 		$this->map(Ids::DIRT, function(Reader $in) : Block{
 			return Blocks::DIRT()
 				->setDirtType(match($value = $in->readString(StateNames::DIRT_TYPE)){
-					StringValues::DIRT_TYPE_NORMAL => DirtType::NORMAL(),
-					StringValues::DIRT_TYPE_COARSE => DirtType::COARSE(),
+					StringValues::DIRT_TYPE_NORMAL => DirtType::NORMAL,
+					StringValues::DIRT_TYPE_COARSE => DirtType::COARSE,
 					default => throw $in->badValueException(StateNames::DIRT_TYPE, $value),
 				});
 		});
-		$this->map(Ids::DIRT_WITH_ROOTS, fn() => Blocks::DIRT()->setDirtType(DirtType::ROOTED()));
+		$this->map(Ids::DIRT_WITH_ROOTS, fn() => Blocks::DIRT()->setDirtType(DirtType::ROOTED));
 		$this->map(Ids::DOUBLE_PLANT, function(Reader $in) : Block{
 			return (match($type = $in->readString(StateNames::DOUBLE_PLANT_TYPE)){
 				StringValues::DOUBLE_PLANT_TYPE_FERN => Blocks::LARGE_FERN(),
@@ -1097,10 +1122,10 @@ private function registerDeserializers() : void{
 			return Blocks::ENDER_CHEST()
 				->setFacing($in->readHorizontalFacing());
 		});
-		$this->map(Ids::EXPOSED_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::EXPOSED()));
-		$this->map(Ids::EXPOSED_CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::EXPOSED()));
-		$this->mapSlab(Ids::EXPOSED_CUT_COPPER_SLAB, Ids::EXPOSED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::EXPOSED()));
-		$this->mapStairs(Ids::EXPOSED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::EXPOSED()));
+		$this->map(Ids::EXPOSED_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::EXPOSED));
+		$this->map(Ids::EXPOSED_CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::EXPOSED));
+		$this->mapSlab(Ids::EXPOSED_CUT_COPPER_SLAB, Ids::EXPOSED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::EXPOSED));
+		$this->mapStairs(Ids::EXPOSED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::EXPOSED));
 		$this->map(Ids::FARMLAND, function(Reader $in) : Block{
 			return Blocks::FARMLAND()
 				->setWetness($in->readBoundedInt(StateNames::MOISTURIZED_AMOUNT, 0, 7));
@@ -1161,6 +1186,11 @@ private function registerDeserializers() : void{
 			return Blocks::LANTERN()
 				->setHanging($in->readBool(StateNames::HANGING));
 		});
+		$this->map(Ids::LARGE_AMETHYST_BUD, function(Reader $in) : Block{
+			return Blocks::AMETHYST_CLUSTER()
+				->setStage(AmethystCluster::STAGE_LARGE_BUD)
+				->setFacing($in->readBlockFace());
+		});
 		$this->map(Ids::LAVA, fn(Reader $in) => Helper::decodeStillLiquid(Blocks::LAVA(), $in));
 		$this->map(Ids::LECTERN, function(Reader $in) : Block{
 			return Blocks::LECTERN()
@@ -1171,14 +1201,14 @@ private function registerDeserializers() : void{
 			return Blocks::LEVER()
 				->setActivated($in->readBool(StateNames::OPEN_BIT))
 				->setFacing(match($value = $in->readString(StateNames::LEVER_DIRECTION)){
-					StringValues::LEVER_DIRECTION_DOWN_NORTH_SOUTH => LeverFacing::DOWN_AXIS_Z(),
-					StringValues::LEVER_DIRECTION_DOWN_EAST_WEST => LeverFacing::DOWN_AXIS_X(),
-					StringValues::LEVER_DIRECTION_UP_NORTH_SOUTH => LeverFacing::UP_AXIS_Z(),
-					StringValues::LEVER_DIRECTION_UP_EAST_WEST => LeverFacing::UP_AXIS_X(),
-					StringValues::LEVER_DIRECTION_NORTH => LeverFacing::NORTH(),
-					StringValues::LEVER_DIRECTION_SOUTH => LeverFacing::SOUTH(),
-					StringValues::LEVER_DIRECTION_WEST => LeverFacing::WEST(),
-					StringValues::LEVER_DIRECTION_EAST => LeverFacing::EAST(),
+					StringValues::LEVER_DIRECTION_DOWN_NORTH_SOUTH => LeverFacing::DOWN_AXIS_Z,
+					StringValues::LEVER_DIRECTION_DOWN_EAST_WEST => LeverFacing::DOWN_AXIS_X,
+					StringValues::LEVER_DIRECTION_UP_NORTH_SOUTH => LeverFacing::UP_AXIS_Z,
+					StringValues::LEVER_DIRECTION_UP_EAST_WEST => LeverFacing::UP_AXIS_X,
+					StringValues::LEVER_DIRECTION_NORTH => LeverFacing::NORTH,
+					StringValues::LEVER_DIRECTION_SOUTH => LeverFacing::SOUTH,
+					StringValues::LEVER_DIRECTION_WEST => LeverFacing::WEST,
+					StringValues::LEVER_DIRECTION_EAST => LeverFacing::EAST,
 					default => throw $in->badValueException(StateNames::LEVER_DIRECTION, $value),
 				});
 		});
@@ -1223,6 +1253,11 @@ private function registerDeserializers() : void{
 			return Blocks::LOOM()
 				->setFacing($in->readLegacyHorizontalFacing());
 		});
+		$this->map(Ids::MEDIUM_AMETHYST_BUD, function(Reader $in) : Block{
+			return Blocks::AMETHYST_CLUSTER()
+				->setStage(AmethystCluster::STAGE_MEDIUM_BUD)
+				->setFacing($in->readBlockFace());
+		});
 		$this->map(Ids::MELON_STEM, fn(Reader $in) => Helper::decodeStem(Blocks::MELON_STEM(), $in));
 		$this->map(Ids::MONSTER_EGG, function(Reader $in) : Block{
 			return match($type = $in->readString(StateNames::MONSTER_EGG_STONE_TYPE)){
@@ -1250,12 +1285,12 @@ private function registerDeserializers() : void{
 				->setAge($in->readBoundedInt(StateNames::AGE, 0, 3));
 		});
 		$this->mapStairs(Ids::NORMAL_STONE_STAIRS, fn() => Blocks::STONE_STAIRS());
-		$this->map(Ids::OCHRE_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::OCHRE())->setAxis($in->readPillarAxis()));
-		$this->map(Ids::OXIDIZED_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::OXIDIZED()));
-		$this->map(Ids::OXIDIZED_CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::OXIDIZED()));
-		$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::OCHRE_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::OCHRE)->setAxis($in->readPillarAxis()));
+		$this->map(Ids::OXIDIZED_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::OXIDIZED));
+		$this->map(Ids::OXIDIZED_CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::OXIDIZED));
+		$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);
@@ -1263,6 +1298,22 @@ private function registerDeserializers() : void{
 				->setFacing($in->readCardinalHorizontalFacing())
 				->setCount(min($growth + 1, PinkPetals::MAX_COUNT));
 		});
+		$this->map(Ids::PITCHER_CROP, function(Reader $in) : Block{
+			$growth = $in->readBoundedInt(StateNames::GROWTH, 0, 7);
+			$top = $in->readBool(StateNames::UPPER_BLOCK_BIT);
+			if($growth <= PitcherCrop::MAX_AGE){
+				//top pitcher crop with age 0-2 is an invalid state
+				//only the bottom half should exist in this case
+				return $top ? Blocks::AIR() : Blocks::PITCHER_CROP()->setAge($growth);
+			}
+			return Blocks::DOUBLE_PITCHER_CROP()
+				->setAge(min($growth - PitcherCrop::MAX_AGE - 1, DoublePitcherCrop::MAX_AGE))
+				->setTop($top);
+		});
+		$this->map(Ids::PITCHER_PLANT, function(Reader $in) : Block{
+			return Blocks::PITCHER_PLANT()
+				->setTop($in->readBool(StateNames::UPPER_BLOCK_BIT));
+		});
 		$this->mapStairs(Ids::POLISHED_ANDESITE_STAIRS, fn() => Blocks::POLISHED_ANDESITE_STAIRS());
 		$this->map(Ids::POLISHED_BASALT, function(Reader $in) : Block{
 			return Blocks::POLISHED_BASALT()
@@ -1437,6 +1488,11 @@ private function registerDeserializers() : void{
 				->setFacing($in->readCardinalHorizontalFacing())
 				->setLit(false);
 		});
+		$this->map(Ids::SMALL_AMETHYST_BUD, function(Reader $in) : Block{
+			return Blocks::AMETHYST_CLUSTER()
+				->setStage(AmethystCluster::STAGE_SMALL_BUD)
+				->setFacing($in->readBlockFace());
+		});
 		$this->map(Ids::SMALL_DRIPLEAF_BLOCK, function(Reader $in) : Block{
 			return Blocks::SMALL_DRIPLEAF()
 				->setFacing($in->readCardinalHorizontalFacing())
@@ -1528,6 +1584,11 @@ private function registerDeserializers() : void{
 			return Blocks::TORCH()
 				->setFacing($in->readTorchFacing());
 		});
+		$this->map(Ids::TORCHFLOWER_CROP, function(Reader $in) : Block{
+			return Blocks::TORCHFLOWER_CROP()
+				//this property can have values 0-7, but only 0-1 are valid
+				->setReady($in->readBoundedInt(StateNames::GROWTH, 0, 7) !== 0);
+		});
 		$this->map(Ids::TRAPPED_CHEST, function(Reader $in) : Block{
 			return Blocks::TRAPPED_CHEST()
 				->setFacing($in->readHorizontalFacing());
@@ -1561,7 +1622,7 @@ private function registerDeserializers() : void{
 		$this->map(Ids::UNPOWERED_COMPARATOR, fn(Reader $in) => Helper::decodeComparator(Blocks::REDSTONE_COMPARATOR(), $in));
 		$this->map(Ids::UNPOWERED_REPEATER, fn(Reader $in) => Helper::decodeRepeater(Blocks::REDSTONE_REPEATER(), $in)
 				->setPowered(false));
-		$this->map(Ids::VERDANT_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::VERDANT())->setAxis($in->readPillarAxis()));
+		$this->map(Ids::VERDANT_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::VERDANT)->setAxis($in->readPillarAxis()));
 		$this->map(Ids::VINE, function(Reader $in) : Block{
 			$vineDirectionFlags = $in->readBoundedInt(StateNames::VINE_DIRECTION_BITS, 0, 15);
 			return Blocks::VINES()
@@ -1575,26 +1636,26 @@ private function registerDeserializers() : void{
 				->setFacing($in->readHorizontalFacing());
 		});
 		$this->map(Ids::WATER, fn(Reader $in) => Helper::decodeStillLiquid(Blocks::WATER(), $in));
-		$this->map(Ids::WAXED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::NONE()));
-		$this->map(Ids::WAXED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::NONE()));
-		$this->mapSlab(Ids::WAXED_CUT_COPPER_SLAB, Ids::WAXED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::NONE()));
-		$this->mapStairs(Ids::WAXED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::NONE()));
-		$this->map(Ids::WAXED_EXPOSED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::EXPOSED()));
-		$this->map(Ids::WAXED_EXPOSED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::EXPOSED()));
-		$this->mapSlab(Ids::WAXED_EXPOSED_CUT_COPPER_SLAB, Ids::WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::EXPOSED()));
-		$this->mapStairs(Ids::WAXED_EXPOSED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::EXPOSED()));
-		$this->map(Ids::WAXED_OXIDIZED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::OXIDIZED()));
-		$this->map(Ids::WAXED_OXIDIZED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::OXIDIZED()));
-		$this->mapSlab(Ids::WAXED_OXIDIZED_CUT_COPPER_SLAB, Ids::WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::OXIDIZED()));
-		$this->mapStairs(Ids::WAXED_OXIDIZED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::OXIDIZED()));
-		$this->map(Ids::WAXED_WEATHERED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::WEATHERED()));
-		$this->map(Ids::WAXED_WEATHERED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::WEATHERED()));
-		$this->mapSlab(Ids::WAXED_WEATHERED_CUT_COPPER_SLAB, Ids::WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::WEATHERED()));
-		$this->mapStairs(Ids::WAXED_WEATHERED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::WEATHERED()));
-		$this->map(Ids::WEATHERED_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::WEATHERED()));
-		$this->map(Ids::WEATHERED_CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::WEATHERED()));
-		$this->mapSlab(Ids::WEATHERED_CUT_COPPER_SLAB, Ids::WEATHERED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::WEATHERED()));
-		$this->mapStairs(Ids::WEATHERED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::WEATHERED()));
+		$this->map(Ids::WAXED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::NONE));
+		$this->map(Ids::WAXED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::NONE));
+		$this->mapSlab(Ids::WAXED_CUT_COPPER_SLAB, Ids::WAXED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::NONE));
+		$this->mapStairs(Ids::WAXED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::NONE));
+		$this->map(Ids::WAXED_EXPOSED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::EXPOSED));
+		$this->map(Ids::WAXED_EXPOSED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::EXPOSED));
+		$this->mapSlab(Ids::WAXED_EXPOSED_CUT_COPPER_SLAB, Ids::WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::EXPOSED));
+		$this->mapStairs(Ids::WAXED_EXPOSED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::EXPOSED));
+		$this->map(Ids::WAXED_OXIDIZED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::OXIDIZED));
+		$this->map(Ids::WAXED_OXIDIZED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::OXIDIZED));
+		$this->mapSlab(Ids::WAXED_OXIDIZED_CUT_COPPER_SLAB, Ids::WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::OXIDIZED));
+		$this->mapStairs(Ids::WAXED_OXIDIZED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::OXIDIZED));
+		$this->map(Ids::WAXED_WEATHERED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::WEATHERED));
+		$this->map(Ids::WAXED_WEATHERED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::WEATHERED));
+		$this->mapSlab(Ids::WAXED_WEATHERED_CUT_COPPER_SLAB, Ids::WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::WEATHERED));
+		$this->mapStairs(Ids::WAXED_WEATHERED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::WEATHERED));
+		$this->map(Ids::WEATHERED_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::WEATHERED));
+		$this->map(Ids::WEATHERED_CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::WEATHERED));
+		$this->mapSlab(Ids::WEATHERED_CUT_COPPER_SLAB, Ids::WEATHERED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::WEATHERED));
+		$this->mapStairs(Ids::WEATHERED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::WEATHERED));
 		$this->map(Ids::WEEPING_VINES, function(Reader $in) : Block{
 			return Blocks::WEEPING_VINES()
 				->setAge($in->readBoundedInt(StateNames::WEEPING_VINES_AGE, 0, 25));
diff --git a/src/data/bedrock/block/convert/BlockStateWriter.php b/src/data/bedrock/block/convert/BlockStateWriter.php
index 6b77a80e14e..f5f34fff651 100644
--- a/src/data/bedrock/block/convert/BlockStateWriter.php
+++ b/src/data/bedrock/block/convert/BlockStateWriter.php
@@ -90,6 +90,20 @@ public function writeFacingDirection(int $value) : self{
 		return $this;
 	}
 
+	/** @return $this */
+	public function writeBlockFace(int $value) : self{
+		$this->writeString(BlockStateNames::MC_BLOCK_FACE, match($value){
+			Facing::DOWN => StringValues::MC_BLOCK_FACE_DOWN,
+			Facing::UP => StringValues::MC_BLOCK_FACE_UP,
+			Facing::NORTH => StringValues::MC_BLOCK_FACE_NORTH,
+			Facing::SOUTH => StringValues::MC_BLOCK_FACE_SOUTH,
+			Facing::WEST => StringValues::MC_BLOCK_FACE_WEST,
+			Facing::EAST => StringValues::MC_BLOCK_FACE_EAST,
+			default => throw new BlockStateSerializeException("Invalid Facing $value")
+		});
+		return $this;
+	}
+
 	/**
 	 * @param int[] $faces
 	 * @phpstan-param array<int, int> $faces
@@ -181,24 +195,23 @@ public function writeCardinalHorizontalFacing(int $value) : self{
 
 	/** @return $this */
 	public function writeColor(DyeColor $color) : self{
-		$this->writeString(BlockStateNames::COLOR, match($color->id()){
-			DyeColor::BLACK()->id() => StringValues::COLOR_BLACK,
-			DyeColor::BLUE()->id() => StringValues::COLOR_BLUE,
-			DyeColor::BROWN()->id() => StringValues::COLOR_BROWN,
-			DyeColor::CYAN()->id() => StringValues::COLOR_CYAN,
-			DyeColor::GRAY()->id() => StringValues::COLOR_GRAY,
-			DyeColor::GREEN()->id() => StringValues::COLOR_GREEN,
-			DyeColor::LIGHT_BLUE()->id() => StringValues::COLOR_LIGHT_BLUE,
-			DyeColor::LIGHT_GRAY()->id() => StringValues::COLOR_SILVER,
-			DyeColor::LIME()->id() => StringValues::COLOR_LIME,
-			DyeColor::MAGENTA()->id() => StringValues::COLOR_MAGENTA,
-			DyeColor::ORANGE()->id() => StringValues::COLOR_ORANGE,
-			DyeColor::PINK()->id() => StringValues::COLOR_PINK,
-			DyeColor::PURPLE()->id() => StringValues::COLOR_PURPLE,
-			DyeColor::RED()->id() => StringValues::COLOR_RED,
-			DyeColor::WHITE()->id() => StringValues::COLOR_WHITE,
-			DyeColor::YELLOW()->id() => StringValues::COLOR_YELLOW,
-			default => throw new BlockStateSerializeException("Invalid Color " . $color->name())
+		$this->writeString(BlockStateNames::COLOR, match($color){
+			DyeColor::BLACK => StringValues::COLOR_BLACK,
+			DyeColor::BLUE => StringValues::COLOR_BLUE,
+			DyeColor::BROWN => StringValues::COLOR_BROWN,
+			DyeColor::CYAN => StringValues::COLOR_CYAN,
+			DyeColor::GRAY => StringValues::COLOR_GRAY,
+			DyeColor::GREEN => StringValues::COLOR_GREEN,
+			DyeColor::LIGHT_BLUE => StringValues::COLOR_LIGHT_BLUE,
+			DyeColor::LIGHT_GRAY => StringValues::COLOR_SILVER,
+			DyeColor::LIME => StringValues::COLOR_LIME,
+			DyeColor::MAGENTA => StringValues::COLOR_MAGENTA,
+			DyeColor::ORANGE => StringValues::COLOR_ORANGE,
+			DyeColor::PINK => StringValues::COLOR_PINK,
+			DyeColor::PURPLE => StringValues::COLOR_PURPLE,
+			DyeColor::RED => StringValues::COLOR_RED,
+			DyeColor::WHITE => StringValues::COLOR_WHITE,
+			DyeColor::YELLOW => StringValues::COLOR_YELLOW,
 		});
 		return $this;
 	}
@@ -246,10 +259,10 @@ public function writePillarAxis(int $axis) : self{
 
 	/** @return $this */
 	public function writeSlabPosition(SlabType $slabType) : self{
-		$this->writeString(BlockStateNames::MC_VERTICAL_HALF, match($slabType->id()){
-			SlabType::TOP()->id() => StringValues::MC_VERTICAL_HALF_TOP,
-			SlabType::BOTTOM()->id() => StringValues::MC_VERTICAL_HALF_BOTTOM,
-			default => throw new BlockStateSerializeException("Invalid slab type " . $slabType->name())
+		$this->writeString(BlockStateNames::MC_VERTICAL_HALF, match($slabType){
+			SlabType::TOP => StringValues::MC_VERTICAL_HALF_TOP,
+			SlabType::BOTTOM => StringValues::MC_VERTICAL_HALF_BOTTOM,
+			default => throw new BlockStateSerializeException("Invalid slab type " . $slabType->name)
 		});
 		return $this;
 	}
@@ -270,39 +283,37 @@ public function writeTorchFacing(int $facing) : self{
 
 	/** @return $this */
 	public function writeLegacyWoodType(WoodType $treeType) : self{
-		$this->writeString(BlockStateNames::WOOD_TYPE, match($treeType->id()){
-			WoodType::OAK()->id() => StringValues::WOOD_TYPE_OAK,
-			WoodType::SPRUCE()->id() => StringValues::WOOD_TYPE_SPRUCE,
-			WoodType::BIRCH()->id() => StringValues::WOOD_TYPE_BIRCH,
-			WoodType::JUNGLE()->id() => StringValues::WOOD_TYPE_JUNGLE,
-			WoodType::ACACIA()->id() => StringValues::WOOD_TYPE_ACACIA,
-			WoodType::DARK_OAK()->id() => StringValues::WOOD_TYPE_DARK_OAK,
-			default => throw new BlockStateSerializeException("Invalid Wood type " . $treeType->name())
+		$this->writeString(BlockStateNames::WOOD_TYPE, match($treeType){
+			WoodType::OAK => StringValues::WOOD_TYPE_OAK,
+			WoodType::SPRUCE => StringValues::WOOD_TYPE_SPRUCE,
+			WoodType::BIRCH => StringValues::WOOD_TYPE_BIRCH,
+			WoodType::JUNGLE => StringValues::WOOD_TYPE_JUNGLE,
+			WoodType::ACACIA => StringValues::WOOD_TYPE_ACACIA,
+			WoodType::DARK_OAK => StringValues::WOOD_TYPE_DARK_OAK,
+			default => throw new BlockStateSerializeException("Invalid legacy wood type " . $treeType->name)
 		});
 		return $this;
 	}
 
 	/** @return $this */
 	public function writeCoralType(CoralType $coralType) : self{
-		$this->writeString(BlockStateNames::CORAL_COLOR, match($coralType->id()){
-			CoralType::TUBE()->id() => StringValues::CORAL_COLOR_BLUE,
-			CoralType::BRAIN()->id() => StringValues::CORAL_COLOR_PINK,
-			CoralType::BUBBLE()->id() => StringValues::CORAL_COLOR_PURPLE,
-			CoralType::FIRE()->id() => StringValues::CORAL_COLOR_RED,
-			CoralType::HORN()->id() => StringValues::CORAL_COLOR_YELLOW,
-			default => throw new BlockStateSerializeException("Invalid Coral type " . $coralType->name())
+		$this->writeString(BlockStateNames::CORAL_COLOR, match($coralType){
+			CoralType::TUBE => StringValues::CORAL_COLOR_BLUE,
+			CoralType::BRAIN => StringValues::CORAL_COLOR_PINK,
+			CoralType::BUBBLE => StringValues::CORAL_COLOR_PURPLE,
+			CoralType::FIRE => StringValues::CORAL_COLOR_RED,
+			CoralType::HORN => StringValues::CORAL_COLOR_YELLOW,
 		});
 		return $this;
 	}
 
 	/** @return $this */
 	public function writeBellAttachmentType(BellAttachmentType $attachmentType) : self{
-		$this->writeString(BlockStateNames::ATTACHMENT, match($attachmentType->id()){
-			BellAttachmentType::FLOOR()->id() => StringValues::ATTACHMENT_STANDING,
-			BellAttachmentType::CEILING()->id() => StringValues::ATTACHMENT_HANGING,
-			BellAttachmentType::ONE_WALL()->id() => StringValues::ATTACHMENT_SIDE,
-			BellAttachmentType::TWO_WALLS()->id() => StringValues::ATTACHMENT_MULTIPLE,
-			default => throw new BlockStateSerializeException("Invalid Bell attachment type " . $attachmentType->name())
+		$this->writeString(BlockStateNames::ATTACHMENT, match($attachmentType){
+			BellAttachmentType::FLOOR => StringValues::ATTACHMENT_STANDING,
+			BellAttachmentType::CEILING => StringValues::ATTACHMENT_HANGING,
+			BellAttachmentType::ONE_WALL => StringValues::ATTACHMENT_SIDE,
+			BellAttachmentType::TWO_WALLS => StringValues::ATTACHMENT_MULTIPLE,
 		});
 		return $this;
 	}
@@ -311,9 +322,8 @@ public function writeBellAttachmentType(BellAttachmentType $attachmentType) : se
 	public function writeWallConnectionType(string $name, ?WallConnectionType $wallConnectionType) : self{
 		$this->writeString($name, match($wallConnectionType){
 			null => StringValues::WALL_CONNECTION_TYPE_EAST_NONE,
-			WallConnectionType::SHORT() => StringValues::WALL_CONNECTION_TYPE_EAST_SHORT,
-			WallConnectionType::TALL() => StringValues::WALL_CONNECTION_TYPE_EAST_TALL,
-			default => throw new BlockStateSerializeException("Invalid Wall connection type " . $wallConnectionType->name())
+			WallConnectionType::SHORT => StringValues::WALL_CONNECTION_TYPE_EAST_SHORT,
+			WallConnectionType::TALL => StringValues::WALL_CONNECTION_TYPE_EAST_TALL,
 		});
 		return $this;
 	}
diff --git a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php
index ccf430b9ccd..6bb3a56b344 100644
--- a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php
+++ b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php
@@ -147,6 +147,7 @@ private function register1to1BlockMappings() : void{
 		$this->map1to1Block(Ids::JUNGLE_DOOR, Blocks::JUNGLE_DOOR());
 		$this->map1to1Block(Ids::MANGROVE_DOOR, Blocks::MANGROVE_DOOR());
 		$this->map1to1Block(Ids::NETHER_WART, Blocks::NETHER_WART());
+		$this->map1to1Block(Ids::PITCHER_POD, Blocks::PITCHER_CROP());
 		$this->map1to1Block(Ids::REPEATER, Blocks::REDSTONE_REPEATER());
 		$this->map1to1Block(Ids::SPRUCE_DOOR, Blocks::SPRUCE_DOOR());
 		$this->map1to1Block(Ids::SUGAR_CANE, Blocks::SUGARCANE());
@@ -359,6 +360,7 @@ private function register1to1ItemMappings() : void{
 		$this->map1to1Item(Ids::STRING, Items::STRING());
 		$this->map1to1Item(Ids::SUGAR, Items::SUGAR());
 		$this->map1to1Item(Ids::SWEET_BERRIES, Items::SWEET_BERRIES());
+		$this->map1to1Item(Ids::TORCHFLOWER_SEEDS, Items::TORCHFLOWER_SEEDS());
 		$this->map1to1Item(Ids::TOTEM_OF_UNDYING, Items::TOTEM());
 		$this->map1to1Item(Ids::TROPICAL_FISH, Items::CLOWNFISH());
 		$this->map1to1Item(Ids::TURTLE_HELMET, Items::TURTLE_HELMET());
@@ -510,7 +512,7 @@ function(SuspiciousStew $item, int $meta) : void{
 	 * complex to implement in a generic way.
 	 */
 	private function registerMiscItemMappings() : void{
-		foreach(DyeColor::getAll() as $color){
+		foreach(DyeColor::cases() as $color){
 			$id = DyeColorIdMap::getInstance()->toItemId($color);
 			$this->deserializer?->map($id, fn() => Items::DYE()->setColor($color));
 		}
diff --git a/src/data/java/GameModeIdMap.php b/src/data/java/GameModeIdMap.php
index 348749b51fa..41258dd66e0 100644
--- a/src/data/java/GameModeIdMap.php
+++ b/src/data/java/GameModeIdMap.php
@@ -26,6 +26,7 @@
 use pocketmine\player\GameMode;
 use pocketmine\utils\SingletonTrait;
 use function array_key_exists;
+use function spl_object_id;
 
 final class GameModeIdMap{
 	use SingletonTrait;
@@ -43,15 +44,15 @@ final class GameModeIdMap{
 	private array $enumToId = [];
 
 	public function __construct(){
-		$this->register(0, GameMode::SURVIVAL());
-		$this->register(1, GameMode::CREATIVE());
-		$this->register(2, GameMode::ADVENTURE());
-		$this->register(3, GameMode::SPECTATOR());
+		$this->register(0, GameMode::SURVIVAL);
+		$this->register(1, GameMode::CREATIVE);
+		$this->register(2, GameMode::ADVENTURE);
+		$this->register(3, GameMode::SPECTATOR);
 	}
 
 	private function register(int $id, GameMode $type) : void{
 		$this->idToEnum[$id] = $type;
-		$this->enumToId[$type->id()] = $id;
+		$this->enumToId[spl_object_id($type)] = $id;
 	}
 
 	public function fromId(int $id) : ?GameMode{
@@ -59,9 +60,10 @@ public function fromId(int $id) : ?GameMode{
 	}
 
 	public function toId(GameMode $type) : int{
-		if(!array_key_exists($type->id(), $this->enumToId)){
-			throw new \InvalidArgumentException("Game mode does not have a mapped ID"); //this should never happen
+		$k = spl_object_id($type);
+		if(!array_key_exists($k, $this->enumToId)){
+			throw new \InvalidArgumentException("Game mode $type->name does not have a mapped ID"); //this should never happen
 		}
-		return $this->enumToId[$type->id()];
+		return $this->enumToId[$k];
 	}
 }
diff --git a/src/data/runtime/RuntimeEnumSizeCalculatorTrait.php b/src/data/runtime/LegacyRuntimeEnumDescriberTrait.php
similarity index 74%
rename from src/data/runtime/RuntimeEnumSizeCalculatorTrait.php
rename to src/data/runtime/LegacyRuntimeEnumDescriberTrait.php
index 2ab62f03e6f..dd35fabfbf1 100644
--- a/src/data/runtime/RuntimeEnumSizeCalculatorTrait.php
+++ b/src/data/runtime/LegacyRuntimeEnumDescriberTrait.php
@@ -24,67 +24,71 @@
 namespace pocketmine\data\runtime;
 
 /**
- * This class is auto-generated. Do not edit it manually.
- * @see build/generate-runtime-enum-serializers.php
+ * Provides backwards-compatible shims for the old codegen'd enum describer methods.
+ * This is kept for plugin backwards compatibility, but these functions should not be used in new code.
+ * @deprecated
  */
-trait RuntimeEnumSizeCalculatorTrait{
+trait LegacyRuntimeEnumDescriberTrait{
 
-	abstract protected function addBits(int $bits) : void;
+	/**
+	 * @phpstan-template T of \UnitEnum
+	 * @phpstan-param T $case
+	 */
+	abstract protected function enum(\UnitEnum &$case) : void;
 
 	public function bellAttachmentType(\pocketmine\block\utils\BellAttachmentType &$value) : void{
-		$this->addBits(2);
+		$this->enum($value);
 	}
 
 	public function copperOxidation(\pocketmine\block\utils\CopperOxidation &$value) : void{
-		$this->addBits(2);
+		$this->enum($value);
 	}
 
 	public function coralType(\pocketmine\block\utils\CoralType &$value) : void{
-		$this->addBits(3);
+		$this->enum($value);
 	}
 
 	public function dirtType(\pocketmine\block\utils\DirtType &$value) : void{
-		$this->addBits(2);
+		$this->enum($value);
 	}
 
 	public function dripleafState(\pocketmine\block\utils\DripleafState &$value) : void{
-		$this->addBits(2);
+		$this->enum($value);
 	}
 
 	public function dyeColor(\pocketmine\block\utils\DyeColor &$value) : void{
-		$this->addBits(4);
+		$this->enum($value);
 	}
 
 	public function froglightType(\pocketmine\block\utils\FroglightType &$value) : void{
-		$this->addBits(2);
+		$this->enum($value);
 	}
 
 	public function leverFacing(\pocketmine\block\utils\LeverFacing &$value) : void{
-		$this->addBits(3);
+		$this->enum($value);
 	}
 
 	public function medicineType(\pocketmine\item\MedicineType &$value) : void{
-		$this->addBits(2);
+		$this->enum($value);
 	}
 
 	public function mobHeadType(\pocketmine\block\utils\MobHeadType &$value) : void{
-		$this->addBits(3);
+		$this->enum($value);
 	}
 
 	public function mushroomBlockType(\pocketmine\block\utils\MushroomBlockType &$value) : void{
-		$this->addBits(4);
+		$this->enum($value);
 	}
 
 	public function potionType(\pocketmine\item\PotionType &$value) : void{
-		$this->addBits(6);
+		$this->enum($value);
 	}
 
 	public function slabType(\pocketmine\block\utils\SlabType &$value) : void{
-		$this->addBits(2);
+		$this->enum($value);
 	}
 
 	public function suspiciousStewType(\pocketmine\item\SuspiciousStewType &$value) : void{
-		$this->addBits(4);
+		$this->enum($value);
 	}
-
 }
diff --git a/src/data/runtime/RuntimeDataDescriber.php b/src/data/runtime/RuntimeDataDescriber.php
index 36822a6f396..04217f5df56 100644
--- a/src/data/runtime/RuntimeDataDescriber.php
+++ b/src/data/runtime/RuntimeDataDescriber.php
@@ -38,8 +38,17 @@
 interface RuntimeDataDescriber extends RuntimeEnumDescriber{
 	public function int(int $bits, int &$value) : void;
 
+	/**
+	 * @deprecated Use {@link RuntimeDataDescriber::boundedIntAuto()} instead.
+	 */
 	public function boundedInt(int $bits, int $min, int $max, int &$value) : void;
 
+	/**
+	 * Same as boundedInt() but automatically calculates the required number of bits from the range.
+	 * The range bounds must be constant.
+	 */
+	public function boundedIntAuto(int $min, int $max, int &$value) : void;
+
 	public function bool(bool &$value) : void;
 
 	public function horizontalFacing(int &$facing) : void;
@@ -71,10 +80,28 @@ public function wallConnections(array &$connections) : void;
 	/**
 	 * @param BrewingStandSlot[] $slots
 	 * @phpstan-param array<int, BrewingStandSlot> $slots
+	 *
+	 * @deprecated Use {@link enumSet()} instead.
 	 */
 	public function brewingStandSlots(array &$slots) : void;
 
 	public function railShape(int &$railShape) : void;
 
 	public function straightOnlyRailShape(int &$railShape) : void;
+
+	/**
+	 * @phpstan-template T of \UnitEnum
+	 * @phpstan-param T $case
+	 */
+	public function enum(\UnitEnum &$case) : void;
+
+	/**
+	 * @param \UnitEnum[] &$set
+	 * @param \UnitEnum[] $allCases
+	 *
+	 * @phpstan-template T of \UnitEnum
+	 * @phpstan-param array<int, T> &$set
+	 * @phpstan-param array<int, T> $allCases
+	 */
+	public function enumSet(array &$set, array $allCases) : void;
 }
diff --git a/src/data/runtime/RuntimeDataReader.php b/src/data/runtime/RuntimeDataReader.php
index 8b0a4b30cde..84239f4113b 100644
--- a/src/data/runtime/RuntimeDataReader.php
+++ b/src/data/runtime/RuntimeDataReader.php
@@ -29,10 +29,13 @@
 use pocketmine\math\Axis;
 use pocketmine\math\Facing;
 use pocketmine\utils\AssumptionFailedError;
+use function get_class;
 use function intdiv;
+use function log;
+use function spl_object_id;
 
 final class RuntimeDataReader implements RuntimeDataDescriber{
-	use RuntimeEnumDeserializerTrait;
+	use LegacyRuntimeEnumDescriberTrait;
 
 	private int $offset = 0;
 
@@ -56,7 +59,20 @@ public function int(int $bits, int &$value) : void{
 		$value = $this->readInt($bits);
 	}
 
-	protected function readBoundedInt(int $bits, int $min, int $max) : int{
+	/**
+	 * @deprecated Use {@link self::boundedIntAuto()} instead.
+	 */
+	public function boundedInt(int $bits, int $min, int $max, int &$value) : void{
+		$offset = $this->offset;
+		$this->boundedIntAuto($min, $max, $value);
+		$actualBits = $this->offset - $offset;
+		if($this->offset !== $offset + $bits){
+			throw new \InvalidArgumentException("Bits should be $actualBits for the given bounds, but received $bits. Use boundedIntAuto() for automatic bits calculation.");
+		}
+	}
+
+	private function readBoundedIntAuto(int $min, int $max) : int{
+		$bits = ((int) log($max - $min, 2)) + 1;
 		$result = $this->readInt($bits) + $min;
 		if($result < $min || $result > $max){
 			throw new InvalidSerializedRuntimeDataException("Value is outside the range $min - $max");
@@ -64,8 +80,8 @@ protected function readBoundedInt(int $bits, int $min, int $max) : int{
 		return $result;
 	}
 
-	public function boundedInt(int $bits, int $min, int $max, int &$value) : void{
-		$value = $this->readBoundedInt($bits, $min, $max);
+	public function boundedIntAuto(int $min, int $max, int &$value) : void{
+		$value = $this->readBoundedIntAuto($min, $max);
 	}
 
 	protected function readBool() : bool{
@@ -160,13 +176,13 @@ public function horizontalAxis(int &$axis) : void{
 	public function wallConnections(array &$connections) : void{
 		$result = [];
 		$offset = 0;
-		$packed = $this->readBoundedInt(7, 0, (3 ** 4) - 1);
+		$packed = $this->readBoundedIntAuto(0, (3 ** 4) - 1);
 		foreach(Facing::HORIZONTAL as $facing){
 			$type = intdiv($packed,  (3 ** $offset)) % 3;
 			if($type !== 0){
 				$result[$facing] = match($type){
-					1 => WallConnectionType::SHORT(),
-					2 => WallConnectionType::TALL(),
+					1 => WallConnectionType::SHORT,
+					2 => WallConnectionType::TALL,
 					default => throw new AssumptionFailedError("Unreachable")
 				};
 			}
@@ -179,20 +195,11 @@ public function wallConnections(array &$connections) : void{
 	/**
 	 * @param BrewingStandSlot[] $slots
 	 * @phpstan-param array<int, BrewingStandSlot> $slots
+	 *
+	 * @deprecated Use {@link enumSet()} instead.
 	 */
 	public function brewingStandSlots(array &$slots) : void{
-		$result = [];
-		foreach([
-			BrewingStandSlot::EAST(),
-			BrewingStandSlot::NORTHWEST(),
-			BrewingStandSlot::SOUTHWEST(),
-		] as $member){
-			if($this->readBool()){
-				$result[$member->id()] = $member;
-			}
-		}
-
-		$slots = $result;
+		$this->enumSet($slots, BrewingStandSlot::cases());
 	}
 
 	public function railShape(int &$railShape) : void{
@@ -213,5 +220,26 @@ public function straightOnlyRailShape(int &$railShape) : void{
 		$railShape = $result;
 	}
 
+	public function enum(\UnitEnum &$case) : void{
+		$metadata = RuntimeEnumMetadata::from($case);
+		$raw = $this->readInt($metadata->bits);
+		$result = $metadata->intToEnum($raw);
+		if($result === null){
+			throw new InvalidSerializedRuntimeDataException("Invalid serialized value $raw for " . get_class($case));
+		}
+
+		$case = $result;
+	}
+
+	public function enumSet(array &$set, array $allCases) : void{
+		$result = [];
+		foreach($allCases as $case){
+			if($this->readBool()){
+				$result[spl_object_id($case)] = $case;
+			}
+		}
+		$set = $result;
+	}
+
 	public function getOffset() : int{ return $this->offset; }
 }
diff --git a/src/data/runtime/RuntimeDataSizeCalculator.php b/src/data/runtime/RuntimeDataSizeCalculator.php
index 87eec2ba849..18c703cb83d 100644
--- a/src/data/runtime/RuntimeDataSizeCalculator.php
+++ b/src/data/runtime/RuntimeDataSizeCalculator.php
@@ -26,9 +26,10 @@
 use pocketmine\block\utils\BrewingStandSlot;
 use pocketmine\math\Facing;
 use function count;
+use function log;
 
 final class RuntimeDataSizeCalculator implements RuntimeDataDescriber{
-	use RuntimeEnumSizeCalculatorTrait;
+	use LegacyRuntimeEnumDescriberTrait;
 
 	private int $bits = 0;
 
@@ -44,8 +45,20 @@ public function int(int $bits, int &$value) : void{
 		$this->addBits($bits);
 	}
 
+	/**
+	 * @deprecated Use {@link self::boundedIntAuto()} instead.
+	 */
 	public function boundedInt(int $bits, int $min, int $max, int &$value) : void{
-		$this->addBits($bits);
+		$currentBits = $this->bits;
+		$this->boundedIntAuto($min, $max, $value);
+		$actualBits = $this->bits - $currentBits;
+		if($actualBits !== $bits){
+			throw new \InvalidArgumentException("Bits should be $actualBits for the given bounds, but received $bits. Use boundedIntAuto() for automatic bits calculation.");
+		}
+	}
+
+	public function boundedIntAuto(int $min, int $max, int &$value) : void{
+		$this->addBits(((int) log($max - $min, 2)) + 1);
 	}
 
 	public function bool(bool &$value) : void{
@@ -85,7 +98,7 @@ public function wallConnections(array &$connections) : void{
 	}
 
 	public function brewingStandSlots(array &$slots) : void{
-		$this->addBits(count(BrewingStandSlot::getAll()));
+		$this->addBits(count(BrewingStandSlot::cases()));
 	}
 
 	public function railShape(int &$railShape) : void{
@@ -95,4 +108,13 @@ public function railShape(int &$railShape) : void{
 	public function straightOnlyRailShape(int &$railShape) : void{
 		$this->addBits(3);
 	}
+
+	public function enum(\UnitEnum &$case) : void{
+		$metadata = RuntimeEnumMetadata::from($case);
+		$this->addBits($metadata->bits);
+	}
+
+	public function enumSet(array &$set, array $allCases) : void{
+		$this->addBits(count($allCases));
+	}
 }
diff --git a/src/data/runtime/RuntimeDataWriter.php b/src/data/runtime/RuntimeDataWriter.php
index e3029673834..0fbdd869f3b 100644
--- a/src/data/runtime/RuntimeDataWriter.php
+++ b/src/data/runtime/RuntimeDataWriter.php
@@ -27,11 +27,12 @@
 use pocketmine\block\utils\WallConnectionType;
 use pocketmine\math\Axis;
 use pocketmine\math\Facing;
-use pocketmine\utils\AssumptionFailedError;
 use function array_flip;
+use function log;
+use function spl_object_id;
 
 final class RuntimeDataWriter implements RuntimeDataDescriber{
-	use RuntimeEnumSerializerTrait;
+	use LegacyRuntimeEnumDescriberTrait;
 
 	private int $value = 0;
 	private int $offset = 0;
@@ -56,15 +57,28 @@ public function int(int $bits, int &$value) : void{
 		$this->writeInt($bits, $value);
 	}
 
-	protected function writeBoundedInt(int $bits, int $min, int $max, int $value) : void{
+	/**
+	 * @deprecated Use {@link self::boundedIntAuto()} instead.
+	 */
+	public function boundedInt(int $bits, int $min, int $max, int &$value) : void{
+		$offset = $this->offset;
+		$this->writeBoundedIntAuto($min, $max, $value);
+		$actualBits = $this->offset - $offset;
+		if($actualBits !== $bits){
+			throw new \InvalidArgumentException("Bits should be $actualBits for the given bounds, but received $bits. Use boundedIntAuto() for automatic bits calculation.");
+		}
+	}
+
+	private function writeBoundedIntAuto(int $min, int $max, int $value) : void{
 		if($value < $min || $value > $max){
 			throw new \InvalidArgumentException("Value $value is outside the range $min - $max");
 		}
+		$bits = ((int) log($max - $min, 2)) + 1;
 		$this->writeInt($bits, $value - $min);
 	}
 
-	public function boundedInt(int $bits, int $min, int $max, int &$value) : void{
-		$this->writeBoundedInt($bits, $min, $max, $value);
+	public function boundedIntAuto(int $min, int $max, int &$value) : void{
+		$this->writeBoundedIntAuto($min, $max, $value);
 	}
 
 	protected function writeBool(bool $value) : void{
@@ -148,27 +162,22 @@ public function wallConnections(array &$connections) : void{
 		foreach(Facing::HORIZONTAL as $facing){
 			$packed += match($connections[$facing] ?? null){
 				null => 0,
-				WallConnectionType::SHORT() => 1,
-				WallConnectionType::TALL() => 2,
-				default => throw new AssumptionFailedError("Unreachable")
+				WallConnectionType::SHORT => 1,
+				WallConnectionType::TALL => 2,
 			} * (3 ** $offset);
 			$offset++;
 		}
-		$this->writeBoundedInt(7, 0, (3 ** 4) - 1, $packed);
+		$this->writeBoundedIntAuto(0, (3 ** 4) - 1, $packed);
 	}
 
 	/**
 	 * @param BrewingStandSlot[] $slots
 	 * @phpstan-param array<int, BrewingStandSlot> $slots
+	 *
+	 * @deprecated Use {@link enumSet()} instead.
 	 */
 	public function brewingStandSlots(array &$slots) : void{
-		foreach([
-			BrewingStandSlot::EAST(),
-			BrewingStandSlot::NORTHWEST(),
-			BrewingStandSlot::SOUTHWEST(),
-		] as $member){
-			$this->writeBool(isset($slots[$member->id()]));
-		}
+		$this->enumSet($slots, BrewingStandSlot::cases());
 	}
 
 	public function railShape(int &$railShape) : void{
@@ -179,6 +188,17 @@ public function straightOnlyRailShape(int &$railShape) : void{
 		$this->int(3, $railShape);
 	}
 
+	public function enum(\UnitEnum &$case) : void{
+		$metadata = RuntimeEnumMetadata::from($case);
+		$this->writeInt($metadata->bits, $metadata->enumToInt($case));
+	}
+
+	public function enumSet(array &$set, array $allCases) : void{
+		foreach($allCases as $case){
+			$this->writeBool(isset($set[spl_object_id($case)]));
+		}
+	}
+
 	public function getValue() : int{ return $this->value; }
 
 	public function getOffset() : int{ return $this->offset; }
diff --git a/src/data/runtime/RuntimeEnumDescriber.php b/src/data/runtime/RuntimeEnumDescriber.php
index 7103017f75a..79550d041bb 100644
--- a/src/data/runtime/RuntimeEnumDescriber.php
+++ b/src/data/runtime/RuntimeEnumDescriber.php
@@ -24,8 +24,9 @@
 namespace pocketmine\data\runtime;
 
 /**
- * This class is auto-generated. Do not edit it manually.
- * @see build/generate-runtime-enum-serializers.php
+ * Provides backwards-compatible shims for the old codegen'd enum describer methods.
+ * This is kept for plugin backwards compatibility, but these functions should not be used in new code.
+ * @deprecated
  */
 interface RuntimeEnumDescriber{
 
diff --git a/src/data/runtime/RuntimeEnumDeserializerTrait.php b/src/data/runtime/RuntimeEnumDeserializerTrait.php
deleted file mode 100644
index 7d80a6f5478..00000000000
--- a/src/data/runtime/RuntimeEnumDeserializerTrait.php
+++ /dev/null
@@ -1,243 +0,0 @@
-<?php
-
-/*
- *
- *  ____            _        _   __  __ _                  __  __ ____
- * |  _ \ ___   ___| | _____| |_|  \/  (_)_ __   ___      |  \/  |  _ \
- * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
- * |  __/ (_) | (__|   <  __/ |_| |  | | | | | |  __/_____| |  | |  __/
- * |_|   \___/ \___|_|\_\___|\__|_|  |_|_|_| |_|\___|     |_|  |_|_|
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * @author PocketMine Team
- * @link http://www.pocketmine.net/
- *
- *
- */
-
-declare(strict_types=1);
-
-namespace pocketmine\data\runtime;
-
-/**
- * This class is auto-generated. Do not edit it manually.
- * @see build/generate-runtime-enum-serializers.php
- */
-trait RuntimeEnumDeserializerTrait{
-
-	abstract protected function readInt(int $bits) : int;
-
-	public function bellAttachmentType(\pocketmine\block\utils\BellAttachmentType &$value) : void{
-		$value = match($this->readInt(2)){
-			0 => \pocketmine\block\utils\BellAttachmentType::CEILING(),
-			1 => \pocketmine\block\utils\BellAttachmentType::FLOOR(),
-			2 => \pocketmine\block\utils\BellAttachmentType::ONE_WALL(),
-			3 => \pocketmine\block\utils\BellAttachmentType::TWO_WALLS(),
-			default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for BellAttachmentType")
-		};
-	}
-
-	public function copperOxidation(\pocketmine\block\utils\CopperOxidation &$value) : void{
-		$value = match($this->readInt(2)){
-			0 => \pocketmine\block\utils\CopperOxidation::EXPOSED(),
-			1 => \pocketmine\block\utils\CopperOxidation::NONE(),
-			2 => \pocketmine\block\utils\CopperOxidation::OXIDIZED(),
-			3 => \pocketmine\block\utils\CopperOxidation::WEATHERED(),
-			default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for CopperOxidation")
-		};
-	}
-
-	public function coralType(\pocketmine\block\utils\CoralType &$value) : void{
-		$value = match($this->readInt(3)){
-			0 => \pocketmine\block\utils\CoralType::BRAIN(),
-			1 => \pocketmine\block\utils\CoralType::BUBBLE(),
-			2 => \pocketmine\block\utils\CoralType::FIRE(),
-			3 => \pocketmine\block\utils\CoralType::HORN(),
-			4 => \pocketmine\block\utils\CoralType::TUBE(),
-			default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for CoralType")
-		};
-	}
-
-	public function dirtType(\pocketmine\block\utils\DirtType &$value) : void{
-		$value = match($this->readInt(2)){
-			0 => \pocketmine\block\utils\DirtType::COARSE(),
-			1 => \pocketmine\block\utils\DirtType::NORMAL(),
-			2 => \pocketmine\block\utils\DirtType::ROOTED(),
-			default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for DirtType")
-		};
-	}
-
-	public function dripleafState(\pocketmine\block\utils\DripleafState &$value) : void{
-		$value = match($this->readInt(2)){
-			0 => \pocketmine\block\utils\DripleafState::FULL_TILT(),
-			1 => \pocketmine\block\utils\DripleafState::PARTIAL_TILT(),
-			2 => \pocketmine\block\utils\DripleafState::STABLE(),
-			3 => \pocketmine\block\utils\DripleafState::UNSTABLE(),
-			default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for DripleafState")
-		};
-	}
-
-	public function dyeColor(\pocketmine\block\utils\DyeColor &$value) : void{
-		$value = match($this->readInt(4)){
-			0 => \pocketmine\block\utils\DyeColor::BLACK(),
-			1 => \pocketmine\block\utils\DyeColor::BLUE(),
-			2 => \pocketmine\block\utils\DyeColor::BROWN(),
-			3 => \pocketmine\block\utils\DyeColor::CYAN(),
-			4 => \pocketmine\block\utils\DyeColor::GRAY(),
-			5 => \pocketmine\block\utils\DyeColor::GREEN(),
-			6 => \pocketmine\block\utils\DyeColor::LIGHT_BLUE(),
-			7 => \pocketmine\block\utils\DyeColor::LIGHT_GRAY(),
-			8 => \pocketmine\block\utils\DyeColor::LIME(),
-			9 => \pocketmine\block\utils\DyeColor::MAGENTA(),
-			10 => \pocketmine\block\utils\DyeColor::ORANGE(),
-			11 => \pocketmine\block\utils\DyeColor::PINK(),
-			12 => \pocketmine\block\utils\DyeColor::PURPLE(),
-			13 => \pocketmine\block\utils\DyeColor::RED(),
-			14 => \pocketmine\block\utils\DyeColor::WHITE(),
-			15 => \pocketmine\block\utils\DyeColor::YELLOW(),
-			default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for DyeColor")
-		};
-	}
-
-	public function froglightType(\pocketmine\block\utils\FroglightType &$value) : void{
-		$value = match($this->readInt(2)){
-			0 => \pocketmine\block\utils\FroglightType::OCHRE(),
-			1 => \pocketmine\block\utils\FroglightType::PEARLESCENT(),
-			2 => \pocketmine\block\utils\FroglightType::VERDANT(),
-			default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for FroglightType")
-		};
-	}
-
-	public function leverFacing(\pocketmine\block\utils\LeverFacing &$value) : void{
-		$value = match($this->readInt(3)){
-			0 => \pocketmine\block\utils\LeverFacing::DOWN_AXIS_X(),
-			1 => \pocketmine\block\utils\LeverFacing::DOWN_AXIS_Z(),
-			2 => \pocketmine\block\utils\LeverFacing::EAST(),
-			3 => \pocketmine\block\utils\LeverFacing::NORTH(),
-			4 => \pocketmine\block\utils\LeverFacing::SOUTH(),
-			5 => \pocketmine\block\utils\LeverFacing::UP_AXIS_X(),
-			6 => \pocketmine\block\utils\LeverFacing::UP_AXIS_Z(),
-			7 => \pocketmine\block\utils\LeverFacing::WEST(),
-			default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for LeverFacing")
-		};
-	}
-
-	public function medicineType(\pocketmine\item\MedicineType &$value) : void{
-		$value = match($this->readInt(2)){
-			0 => \pocketmine\item\MedicineType::ANTIDOTE(),
-			1 => \pocketmine\item\MedicineType::ELIXIR(),
-			2 => \pocketmine\item\MedicineType::EYE_DROPS(),
-			3 => \pocketmine\item\MedicineType::TONIC(),
-			default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for MedicineType")
-		};
-	}
-
-	public function mobHeadType(\pocketmine\block\utils\MobHeadType &$value) : void{
-		$value = match($this->readInt(3)){
-			0 => \pocketmine\block\utils\MobHeadType::CREEPER(),
-			1 => \pocketmine\block\utils\MobHeadType::DRAGON(),
-			2 => \pocketmine\block\utils\MobHeadType::PIGLIN(),
-			3 => \pocketmine\block\utils\MobHeadType::PLAYER(),
-			4 => \pocketmine\block\utils\MobHeadType::SKELETON(),
-			5 => \pocketmine\block\utils\MobHeadType::WITHER_SKELETON(),
-			6 => \pocketmine\block\utils\MobHeadType::ZOMBIE(),
-			default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for MobHeadType")
-		};
-	}
-
-	public function mushroomBlockType(\pocketmine\block\utils\MushroomBlockType &$value) : void{
-		$value = match($this->readInt(4)){
-			0 => \pocketmine\block\utils\MushroomBlockType::ALL_CAP(),
-			1 => \pocketmine\block\utils\MushroomBlockType::CAP_EAST(),
-			2 => \pocketmine\block\utils\MushroomBlockType::CAP_MIDDLE(),
-			3 => \pocketmine\block\utils\MushroomBlockType::CAP_NORTH(),
-			4 => \pocketmine\block\utils\MushroomBlockType::CAP_NORTHEAST(),
-			5 => \pocketmine\block\utils\MushroomBlockType::CAP_NORTHWEST(),
-			6 => \pocketmine\block\utils\MushroomBlockType::CAP_SOUTH(),
-			7 => \pocketmine\block\utils\MushroomBlockType::CAP_SOUTHEAST(),
-			8 => \pocketmine\block\utils\MushroomBlockType::CAP_SOUTHWEST(),
-			9 => \pocketmine\block\utils\MushroomBlockType::CAP_WEST(),
-			10 => \pocketmine\block\utils\MushroomBlockType::PORES(),
-			default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for MushroomBlockType")
-		};
-	}
-
-	public function potionType(\pocketmine\item\PotionType &$value) : void{
-		$value = match($this->readInt(6)){
-			0 => \pocketmine\item\PotionType::AWKWARD(),
-			1 => \pocketmine\item\PotionType::FIRE_RESISTANCE(),
-			2 => \pocketmine\item\PotionType::HARMING(),
-			3 => \pocketmine\item\PotionType::HEALING(),
-			4 => \pocketmine\item\PotionType::INVISIBILITY(),
-			5 => \pocketmine\item\PotionType::LEAPING(),
-			6 => \pocketmine\item\PotionType::LONG_FIRE_RESISTANCE(),
-			7 => \pocketmine\item\PotionType::LONG_INVISIBILITY(),
-			8 => \pocketmine\item\PotionType::LONG_LEAPING(),
-			9 => \pocketmine\item\PotionType::LONG_MUNDANE(),
-			10 => \pocketmine\item\PotionType::LONG_NIGHT_VISION(),
-			11 => \pocketmine\item\PotionType::LONG_POISON(),
-			12 => \pocketmine\item\PotionType::LONG_REGENERATION(),
-			13 => \pocketmine\item\PotionType::LONG_SLOWNESS(),
-			14 => \pocketmine\item\PotionType::LONG_SLOW_FALLING(),
-			15 => \pocketmine\item\PotionType::LONG_STRENGTH(),
-			16 => \pocketmine\item\PotionType::LONG_SWIFTNESS(),
-			17 => \pocketmine\item\PotionType::LONG_TURTLE_MASTER(),
-			18 => \pocketmine\item\PotionType::LONG_WATER_BREATHING(),
-			19 => \pocketmine\item\PotionType::LONG_WEAKNESS(),
-			20 => \pocketmine\item\PotionType::MUNDANE(),
-			21 => \pocketmine\item\PotionType::NIGHT_VISION(),
-			22 => \pocketmine\item\PotionType::POISON(),
-			23 => \pocketmine\item\PotionType::REGENERATION(),
-			24 => \pocketmine\item\PotionType::SLOWNESS(),
-			25 => \pocketmine\item\PotionType::SLOW_FALLING(),
-			26 => \pocketmine\item\PotionType::STRENGTH(),
-			27 => \pocketmine\item\PotionType::STRONG_HARMING(),
-			28 => \pocketmine\item\PotionType::STRONG_HEALING(),
-			29 => \pocketmine\item\PotionType::STRONG_LEAPING(),
-			30 => \pocketmine\item\PotionType::STRONG_POISON(),
-			31 => \pocketmine\item\PotionType::STRONG_REGENERATION(),
-			32 => \pocketmine\item\PotionType::STRONG_SLOWNESS(),
-			33 => \pocketmine\item\PotionType::STRONG_STRENGTH(),
-			34 => \pocketmine\item\PotionType::STRONG_SWIFTNESS(),
-			35 => \pocketmine\item\PotionType::STRONG_TURTLE_MASTER(),
-			36 => \pocketmine\item\PotionType::SWIFTNESS(),
-			37 => \pocketmine\item\PotionType::THICK(),
-			38 => \pocketmine\item\PotionType::TURTLE_MASTER(),
-			39 => \pocketmine\item\PotionType::WATER(),
-			40 => \pocketmine\item\PotionType::WATER_BREATHING(),
-			41 => \pocketmine\item\PotionType::WEAKNESS(),
-			42 => \pocketmine\item\PotionType::WITHER(),
-			default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for PotionType")
-		};
-	}
-
-	public function slabType(\pocketmine\block\utils\SlabType &$value) : void{
-		$value = match($this->readInt(2)){
-			0 => \pocketmine\block\utils\SlabType::BOTTOM(),
-			1 => \pocketmine\block\utils\SlabType::DOUBLE(),
-			2 => \pocketmine\block\utils\SlabType::TOP(),
-			default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for SlabType")
-		};
-	}
-
-	public function suspiciousStewType(\pocketmine\item\SuspiciousStewType &$value) : void{
-		$value = match($this->readInt(4)){
-			0 => \pocketmine\item\SuspiciousStewType::ALLIUM(),
-			1 => \pocketmine\item\SuspiciousStewType::AZURE_BLUET(),
-			2 => \pocketmine\item\SuspiciousStewType::BLUE_ORCHID(),
-			3 => \pocketmine\item\SuspiciousStewType::CORNFLOWER(),
-			4 => \pocketmine\item\SuspiciousStewType::DANDELION(),
-			5 => \pocketmine\item\SuspiciousStewType::LILY_OF_THE_VALLEY(),
-			6 => \pocketmine\item\SuspiciousStewType::OXEYE_DAISY(),
-			7 => \pocketmine\item\SuspiciousStewType::POPPY(),
-			8 => \pocketmine\item\SuspiciousStewType::TULIP(),
-			9 => \pocketmine\item\SuspiciousStewType::WITHER_ROSE(),
-			default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for SuspiciousStewType")
-		};
-	}
-
-}
diff --git a/src/data/runtime/RuntimeEnumMetadata.php b/src/data/runtime/RuntimeEnumMetadata.php
new file mode 100644
index 00000000000..261b7a1bc3e
--- /dev/null
+++ b/src/data/runtime/RuntimeEnumMetadata.php
@@ -0,0 +1,114 @@
+<?php
+
+/*
+ *
+ *  ____            _        _   __  __ _                  __  __ ____
+ * |  _ \ ___   ___| | _____| |_|  \/  (_)_ __   ___      |  \/  |  _ \
+ * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
+ * |  __/ (_) | (__|   <  __/ |_| |  | | | | | |  __/_____| |  | |  __/
+ * |_|   \___/ \___|_|\_\___|\__|_|  |_|_|_| |_|\___|     |_|  |_|_|
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * @author PocketMine Team
+ * @link http://www.pocketmine.net/
+ *
+ *
+ */
+
+declare(strict_types=1);
+
+namespace pocketmine\data\runtime;
+
+use function array_values;
+use function ceil;
+use function count;
+use function log;
+use function spl_object_id;
+use function usort;
+
+/**
+ * A big hack to allow lazily associating enum cases with packed bit values for RuntimeDataDescriber :)
+ *
+ * @internal
+ * @phpstan-template T of \UnitEnum
+ */
+final class RuntimeEnumMetadata{
+	public readonly int $bits;
+
+	/**
+	 * @var object[]
+	 * @phpstan-var list<T>
+	 */
+	private readonly array $intToEnum;
+	/**
+	 * @var int[]
+	 * @phpstan-var array<int, int>
+	 */
+	private readonly array $enumToInt;
+
+	/**
+	 * @param \UnitEnum[] $members
+	 * @phpstan-param list<T> $members
+	 */
+	public function __construct(
+		array $members
+	){
+		usort($members, fn(\UnitEnum $a, \UnitEnum $b) => $a->name <=> $b->name); //sort by name to ensure consistent ordering (and thus consistent bit assignments)
+
+		$this->bits = (int) ceil(log(count($members), 2));
+		$this->intToEnum = array_values($members);
+
+		$reversed = [];
+		foreach($this->intToEnum as $int => $enum){
+			$reversed[spl_object_id($enum)] = $int;
+		}
+
+		$this->enumToInt = $reversed;
+	}
+
+	/**
+	 * @phpstan-return T|null
+	 */
+	public function intToEnum(int $value) : ?object{
+		return $this->intToEnum[$value] ?? null;
+	}
+
+	/**
+	 * @phpstan-param T $enum
+	 */
+	public function enumToInt(object $enum) : int{
+		return $this->enumToInt[spl_object_id($enum)];
+	}
+
+	/**
+	 * @var self[]
+	 * @phpstan-var array<class-string, object>
+	 */
+	private static array $cache = [];
+
+	/**
+	 * @phpstan-template TEnum of \UnitEnum
+	 * @phpstan-param TEnum $case
+	 *
+	 * @phpstan-return self<TEnum>
+	 */
+	public static function from(\UnitEnum $case) : self{
+		$class = $case::class;
+		/** @phpstan-var self<TEnum>|null $metadata */
+		$metadata = self::$cache[$class] ?? null;
+		if($metadata === null){
+			/**
+			 * PHPStan can't infer this correctly :( https://github.com/phpstan/phpstan/issues/7162
+			 * @phpstan-var list<TEnum> $cases
+			 */
+			$cases = $case::cases();
+			self::$cache[$class] = $metadata = new self($cases);
+		}
+
+		return $metadata;
+	}
+}
diff --git a/src/data/runtime/RuntimeEnumSerializerTrait.php b/src/data/runtime/RuntimeEnumSerializerTrait.php
deleted file mode 100644
index 8c3d671181a..00000000000
--- a/src/data/runtime/RuntimeEnumSerializerTrait.php
+++ /dev/null
@@ -1,243 +0,0 @@
-<?php
-
-/*
- *
- *  ____            _        _   __  __ _                  __  __ ____
- * |  _ \ ___   ___| | _____| |_|  \/  (_)_ __   ___      |  \/  |  _ \
- * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
- * |  __/ (_) | (__|   <  __/ |_| |  | | | | | |  __/_____| |  | |  __/
- * |_|   \___/ \___|_|\_\___|\__|_|  |_|_|_| |_|\___|     |_|  |_|_|
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * @author PocketMine Team
- * @link http://www.pocketmine.net/
- *
- *
- */
-
-declare(strict_types=1);
-
-namespace pocketmine\data\runtime;
-
-/**
- * This class is auto-generated. Do not edit it manually.
- * @see build/generate-runtime-enum-serializers.php
- */
-trait RuntimeEnumSerializerTrait{
-
-	abstract protected function writeInt(int $bits, int $value) : void;
-
-	public function bellAttachmentType(\pocketmine\block\utils\BellAttachmentType &$value) : void{
-		$this->writeInt(2, match($value){
-			\pocketmine\block\utils\BellAttachmentType::CEILING() => 0,
-			\pocketmine\block\utils\BellAttachmentType::FLOOR() => 1,
-			\pocketmine\block\utils\BellAttachmentType::ONE_WALL() => 2,
-			\pocketmine\block\utils\BellAttachmentType::TWO_WALLS() => 3,
-			default => throw new \pocketmine\utils\AssumptionFailedError("All BellAttachmentType cases should be covered")
-		});
-	}
-
-	public function copperOxidation(\pocketmine\block\utils\CopperOxidation &$value) : void{
-		$this->writeInt(2, match($value){
-			\pocketmine\block\utils\CopperOxidation::EXPOSED() => 0,
-			\pocketmine\block\utils\CopperOxidation::NONE() => 1,
-			\pocketmine\block\utils\CopperOxidation::OXIDIZED() => 2,
-			\pocketmine\block\utils\CopperOxidation::WEATHERED() => 3,
-			default => throw new \pocketmine\utils\AssumptionFailedError("All CopperOxidation cases should be covered")
-		});
-	}
-
-	public function coralType(\pocketmine\block\utils\CoralType &$value) : void{
-		$this->writeInt(3, match($value){
-			\pocketmine\block\utils\CoralType::BRAIN() => 0,
-			\pocketmine\block\utils\CoralType::BUBBLE() => 1,
-			\pocketmine\block\utils\CoralType::FIRE() => 2,
-			\pocketmine\block\utils\CoralType::HORN() => 3,
-			\pocketmine\block\utils\CoralType::TUBE() => 4,
-			default => throw new \pocketmine\utils\AssumptionFailedError("All CoralType cases should be covered")
-		});
-	}
-
-	public function dirtType(\pocketmine\block\utils\DirtType &$value) : void{
-		$this->writeInt(2, match($value){
-			\pocketmine\block\utils\DirtType::COARSE() => 0,
-			\pocketmine\block\utils\DirtType::NORMAL() => 1,
-			\pocketmine\block\utils\DirtType::ROOTED() => 2,
-			default => throw new \pocketmine\utils\AssumptionFailedError("All DirtType cases should be covered")
-		});
-	}
-
-	public function dripleafState(\pocketmine\block\utils\DripleafState &$value) : void{
-		$this->writeInt(2, match($value){
-			\pocketmine\block\utils\DripleafState::FULL_TILT() => 0,
-			\pocketmine\block\utils\DripleafState::PARTIAL_TILT() => 1,
-			\pocketmine\block\utils\DripleafState::STABLE() => 2,
-			\pocketmine\block\utils\DripleafState::UNSTABLE() => 3,
-			default => throw new \pocketmine\utils\AssumptionFailedError("All DripleafState cases should be covered")
-		});
-	}
-
-	public function dyeColor(\pocketmine\block\utils\DyeColor &$value) : void{
-		$this->writeInt(4, match($value){
-			\pocketmine\block\utils\DyeColor::BLACK() => 0,
-			\pocketmine\block\utils\DyeColor::BLUE() => 1,
-			\pocketmine\block\utils\DyeColor::BROWN() => 2,
-			\pocketmine\block\utils\DyeColor::CYAN() => 3,
-			\pocketmine\block\utils\DyeColor::GRAY() => 4,
-			\pocketmine\block\utils\DyeColor::GREEN() => 5,
-			\pocketmine\block\utils\DyeColor::LIGHT_BLUE() => 6,
-			\pocketmine\block\utils\DyeColor::LIGHT_GRAY() => 7,
-			\pocketmine\block\utils\DyeColor::LIME() => 8,
-			\pocketmine\block\utils\DyeColor::MAGENTA() => 9,
-			\pocketmine\block\utils\DyeColor::ORANGE() => 10,
-			\pocketmine\block\utils\DyeColor::PINK() => 11,
-			\pocketmine\block\utils\DyeColor::PURPLE() => 12,
-			\pocketmine\block\utils\DyeColor::RED() => 13,
-			\pocketmine\block\utils\DyeColor::WHITE() => 14,
-			\pocketmine\block\utils\DyeColor::YELLOW() => 15,
-			default => throw new \pocketmine\utils\AssumptionFailedError("All DyeColor cases should be covered")
-		});
-	}
-
-	public function froglightType(\pocketmine\block\utils\FroglightType &$value) : void{
-		$this->writeInt(2, match($value){
-			\pocketmine\block\utils\FroglightType::OCHRE() => 0,
-			\pocketmine\block\utils\FroglightType::PEARLESCENT() => 1,
-			\pocketmine\block\utils\FroglightType::VERDANT() => 2,
-			default => throw new \pocketmine\utils\AssumptionFailedError("All FroglightType cases should be covered")
-		});
-	}
-
-	public function leverFacing(\pocketmine\block\utils\LeverFacing &$value) : void{
-		$this->writeInt(3, match($value){
-			\pocketmine\block\utils\LeverFacing::DOWN_AXIS_X() => 0,
-			\pocketmine\block\utils\LeverFacing::DOWN_AXIS_Z() => 1,
-			\pocketmine\block\utils\LeverFacing::EAST() => 2,
-			\pocketmine\block\utils\LeverFacing::NORTH() => 3,
-			\pocketmine\block\utils\LeverFacing::SOUTH() => 4,
-			\pocketmine\block\utils\LeverFacing::UP_AXIS_X() => 5,
-			\pocketmine\block\utils\LeverFacing::UP_AXIS_Z() => 6,
-			\pocketmine\block\utils\LeverFacing::WEST() => 7,
-			default => throw new \pocketmine\utils\AssumptionFailedError("All LeverFacing cases should be covered")
-		});
-	}
-
-	public function medicineType(\pocketmine\item\MedicineType &$value) : void{
-		$this->writeInt(2, match($value){
-			\pocketmine\item\MedicineType::ANTIDOTE() => 0,
-			\pocketmine\item\MedicineType::ELIXIR() => 1,
-			\pocketmine\item\MedicineType::EYE_DROPS() => 2,
-			\pocketmine\item\MedicineType::TONIC() => 3,
-			default => throw new \pocketmine\utils\AssumptionFailedError("All MedicineType cases should be covered")
-		});
-	}
-
-	public function mobHeadType(\pocketmine\block\utils\MobHeadType &$value) : void{
-		$this->writeInt(3, match($value){
-			\pocketmine\block\utils\MobHeadType::CREEPER() => 0,
-			\pocketmine\block\utils\MobHeadType::DRAGON() => 1,
-			\pocketmine\block\utils\MobHeadType::PIGLIN() => 2,
-			\pocketmine\block\utils\MobHeadType::PLAYER() => 3,
-			\pocketmine\block\utils\MobHeadType::SKELETON() => 4,
-			\pocketmine\block\utils\MobHeadType::WITHER_SKELETON() => 5,
-			\pocketmine\block\utils\MobHeadType::ZOMBIE() => 6,
-			default => throw new \pocketmine\utils\AssumptionFailedError("All MobHeadType cases should be covered")
-		});
-	}
-
-	public function mushroomBlockType(\pocketmine\block\utils\MushroomBlockType &$value) : void{
-		$this->writeInt(4, match($value){
-			\pocketmine\block\utils\MushroomBlockType::ALL_CAP() => 0,
-			\pocketmine\block\utils\MushroomBlockType::CAP_EAST() => 1,
-			\pocketmine\block\utils\MushroomBlockType::CAP_MIDDLE() => 2,
-			\pocketmine\block\utils\MushroomBlockType::CAP_NORTH() => 3,
-			\pocketmine\block\utils\MushroomBlockType::CAP_NORTHEAST() => 4,
-			\pocketmine\block\utils\MushroomBlockType::CAP_NORTHWEST() => 5,
-			\pocketmine\block\utils\MushroomBlockType::CAP_SOUTH() => 6,
-			\pocketmine\block\utils\MushroomBlockType::CAP_SOUTHEAST() => 7,
-			\pocketmine\block\utils\MushroomBlockType::CAP_SOUTHWEST() => 8,
-			\pocketmine\block\utils\MushroomBlockType::CAP_WEST() => 9,
-			\pocketmine\block\utils\MushroomBlockType::PORES() => 10,
-			default => throw new \pocketmine\utils\AssumptionFailedError("All MushroomBlockType cases should be covered")
-		});
-	}
-
-	public function potionType(\pocketmine\item\PotionType &$value) : void{
-		$this->writeInt(6, match($value){
-			\pocketmine\item\PotionType::AWKWARD() => 0,
-			\pocketmine\item\PotionType::FIRE_RESISTANCE() => 1,
-			\pocketmine\item\PotionType::HARMING() => 2,
-			\pocketmine\item\PotionType::HEALING() => 3,
-			\pocketmine\item\PotionType::INVISIBILITY() => 4,
-			\pocketmine\item\PotionType::LEAPING() => 5,
-			\pocketmine\item\PotionType::LONG_FIRE_RESISTANCE() => 6,
-			\pocketmine\item\PotionType::LONG_INVISIBILITY() => 7,
-			\pocketmine\item\PotionType::LONG_LEAPING() => 8,
-			\pocketmine\item\PotionType::LONG_MUNDANE() => 9,
-			\pocketmine\item\PotionType::LONG_NIGHT_VISION() => 10,
-			\pocketmine\item\PotionType::LONG_POISON() => 11,
-			\pocketmine\item\PotionType::LONG_REGENERATION() => 12,
-			\pocketmine\item\PotionType::LONG_SLOWNESS() => 13,
-			\pocketmine\item\PotionType::LONG_SLOW_FALLING() => 14,
-			\pocketmine\item\PotionType::LONG_STRENGTH() => 15,
-			\pocketmine\item\PotionType::LONG_SWIFTNESS() => 16,
-			\pocketmine\item\PotionType::LONG_TURTLE_MASTER() => 17,
-			\pocketmine\item\PotionType::LONG_WATER_BREATHING() => 18,
-			\pocketmine\item\PotionType::LONG_WEAKNESS() => 19,
-			\pocketmine\item\PotionType::MUNDANE() => 20,
-			\pocketmine\item\PotionType::NIGHT_VISION() => 21,
-			\pocketmine\item\PotionType::POISON() => 22,
-			\pocketmine\item\PotionType::REGENERATION() => 23,
-			\pocketmine\item\PotionType::SLOWNESS() => 24,
-			\pocketmine\item\PotionType::SLOW_FALLING() => 25,
-			\pocketmine\item\PotionType::STRENGTH() => 26,
-			\pocketmine\item\PotionType::STRONG_HARMING() => 27,
-			\pocketmine\item\PotionType::STRONG_HEALING() => 28,
-			\pocketmine\item\PotionType::STRONG_LEAPING() => 29,
-			\pocketmine\item\PotionType::STRONG_POISON() => 30,
-			\pocketmine\item\PotionType::STRONG_REGENERATION() => 31,
-			\pocketmine\item\PotionType::STRONG_SLOWNESS() => 32,
-			\pocketmine\item\PotionType::STRONG_STRENGTH() => 33,
-			\pocketmine\item\PotionType::STRONG_SWIFTNESS() => 34,
-			\pocketmine\item\PotionType::STRONG_TURTLE_MASTER() => 35,
-			\pocketmine\item\PotionType::SWIFTNESS() => 36,
-			\pocketmine\item\PotionType::THICK() => 37,
-			\pocketmine\item\PotionType::TURTLE_MASTER() => 38,
-			\pocketmine\item\PotionType::WATER() => 39,
-			\pocketmine\item\PotionType::WATER_BREATHING() => 40,
-			\pocketmine\item\PotionType::WEAKNESS() => 41,
-			\pocketmine\item\PotionType::WITHER() => 42,
-			default => throw new \pocketmine\utils\AssumptionFailedError("All PotionType cases should be covered")
-		});
-	}
-
-	public function slabType(\pocketmine\block\utils\SlabType &$value) : void{
-		$this->writeInt(2, match($value){
-			\pocketmine\block\utils\SlabType::BOTTOM() => 0,
-			\pocketmine\block\utils\SlabType::DOUBLE() => 1,
-			\pocketmine\block\utils\SlabType::TOP() => 2,
-			default => throw new \pocketmine\utils\AssumptionFailedError("All SlabType cases should be covered")
-		});
-	}
-
-	public function suspiciousStewType(\pocketmine\item\SuspiciousStewType &$value) : void{
-		$this->writeInt(4, match($value){
-			\pocketmine\item\SuspiciousStewType::ALLIUM() => 0,
-			\pocketmine\item\SuspiciousStewType::AZURE_BLUET() => 1,
-			\pocketmine\item\SuspiciousStewType::BLUE_ORCHID() => 2,
-			\pocketmine\item\SuspiciousStewType::CORNFLOWER() => 3,
-			\pocketmine\item\SuspiciousStewType::DANDELION() => 4,
-			\pocketmine\item\SuspiciousStewType::LILY_OF_THE_VALLEY() => 5,
-			\pocketmine\item\SuspiciousStewType::OXEYE_DAISY() => 6,
-			\pocketmine\item\SuspiciousStewType::POPPY() => 7,
-			\pocketmine\item\SuspiciousStewType::TULIP() => 8,
-			\pocketmine\item\SuspiciousStewType::WITHER_ROSE() => 9,
-			default => throw new \pocketmine\utils\AssumptionFailedError("All SuspiciousStewType cases should be covered")
-		});
-	}
-
-}
diff --git a/src/entity/Entity.php b/src/entity/Entity.php
index 7e4d7cbdef4..d26a06d92b3 100644
--- a/src/entity/Entity.php
+++ b/src/entity/Entity.php
@@ -842,7 +842,7 @@ protected function tryChangeMovement() : void{
 
 	protected function checkObstruction(float $x, float $y, float $z) : bool{
 		$world = $this->getWorld();
-		if(count($world->getCollisionBoxes($this, $this->getBoundingBox(), false)) === 0){
+		if(count($world->getBlockCollisionBoxes($this->boundingBox)) === 0){
 			return false;
 		}
 
@@ -1144,7 +1144,7 @@ protected function move(float $dx, float $dy, float $dz) : void{
 
 			assert(abs($dx) <= 20 && abs($dy) <= 20 && abs($dz) <= 20, "Movement distance is excessive: dx=$dx, dy=$dy, dz=$dz");
 
-			$list = $this->getWorld()->getCollisionBoxes($this, $moveBB->addCoord($dx, $dy, $dz), false);
+			$list = $this->getWorld()->getBlockCollisionBoxes($moveBB->addCoord($dx, $dy, $dz));
 
 			foreach($list as $bb){
 				$dy = $bb->calculateYOffset($moveBB, $dy);
@@ -1176,7 +1176,7 @@ protected function move(float $dx, float $dy, float $dz) : void{
 
 				$stepBB = clone $this->boundingBox;
 
-				$list = $this->getWorld()->getCollisionBoxes($this, $stepBB->addCoord($dx, $dy, $dz), false);
+				$list = $this->getWorld()->getBlockCollisionBoxes($stepBB->addCoord($dx, $dy, $dz));
 				foreach($list as $bb){
 					$dy = $bb->calculateYOffset($stepBB, $dy);
 				}
diff --git a/src/entity/projectile/SplashPotion.php b/src/entity/projectile/SplashPotion.php
index 17e4d8af71a..f4635cad77c 100644
--- a/src/entity/projectile/SplashPotion.php
+++ b/src/entity/projectile/SplashPotion.php
@@ -129,7 +129,7 @@ protected function onHit(ProjectileHitEvent $event) : void{
 			}else{
 				//TODO: lingering potions
 			}
-		}elseif($event instanceof ProjectileHitBlockEvent && $this->getPotionType()->equals(PotionType::WATER())){
+		}elseif($event instanceof ProjectileHitBlockEvent && $this->getPotionType() === PotionType::WATER){
 			$blockIn = $event->getBlockHit()->getSide($event->getRayTraceResult()->getHitFace());
 
 			if($blockIn->hasTypeTag(BlockTypeTags::FIRE)){
diff --git a/src/item/Armor.php b/src/item/Armor.php
index e52732caf6d..e9667a8a89a 100644
--- a/src/item/Armor.php
+++ b/src/item/Armor.php
@@ -150,7 +150,7 @@ public function onClickAir(Player $player, Vector3 $directionVector, array &$ret
 			//if the stack size was bigger than 1 (usually won't happen, but might be caused by plugins)
 			$returnedItems[] = $thisCopy;
 		}
-		return ItemUseResult::SUCCESS();
+		return ItemUseResult::SUCCESS;
 	}
 
 	protected function deserializeCompoundTag(CompoundTag $tag) : void{
diff --git a/src/item/Banner.php b/src/item/Banner.php
index 250f2099efa..2fc53f5ae60 100644
--- a/src/item/Banner.php
+++ b/src/item/Banner.php
@@ -23,7 +23,6 @@
 
 namespace pocketmine\item;
 
-use pocketmine\block\Block;
 use pocketmine\block\tile\Banner as TileBanner;
 use pocketmine\block\utils\BannerPatternLayer;
 use pocketmine\block\utils\DyeColor;
@@ -40,7 +39,7 @@ class Banner extends ItemBlockWallOrFloor{
 	public const TAG_PATTERN_COLOR = TileBanner::TAG_PATTERN_COLOR;
 	public const TAG_PATTERN_NAME = TileBanner::TAG_PATTERN_NAME;
 
-	private DyeColor $color;
+	private DyeColor $color = DyeColor::BLACK;
 
 	/**
 	 * @var BannerPatternLayer[]
@@ -48,11 +47,6 @@ class Banner extends ItemBlockWallOrFloor{
 	 */
 	private array $patterns = [];
 
-	public function __construct(ItemIdentifier $identifier, Block $floorVariant, Block $wallVariant){
-		parent::__construct($identifier, $floorVariant, $wallVariant);
-		$this->color = DyeColor::BLACK();
-	}
-
 	public function getColor() : DyeColor{
 		return $this->color;
 	}
@@ -64,7 +58,7 @@ public function setColor(DyeColor $color) : self{
 	}
 
 	protected function describeState(RuntimeDataDescriber $w) : void{
-		$w->dyeColor($this->color);
+		$w->enum($this->color);
 	}
 
 	/**
@@ -102,7 +96,7 @@ protected function deserializeCompoundTag(CompoundTag $tag) : void{
 		if($patterns !== null && $patterns->getTagType() === NBT::TAG_Compound){
 			/** @var CompoundTag $t */
 			foreach($patterns as $t){
-				$patternColor = $colorIdMap->fromInvertedId($t->getInt(self::TAG_PATTERN_COLOR)) ?? DyeColor::BLACK(); //TODO: missing pattern colour should be an error
+				$patternColor = $colorIdMap->fromInvertedId($t->getInt(self::TAG_PATTERN_COLOR)) ?? DyeColor::BLACK; //TODO: missing pattern colour should be an error
 				$patternType = $patternIdMap->fromId($t->getString(self::TAG_PATTERN_NAME));
 				if($patternType === null){
 					continue; //TODO: this should be an error
diff --git a/src/item/BoatType.php b/src/item/BoatType.php
index 5ef0c9255b4..518a1324e7d 100644
--- a/src/item/BoatType.php
+++ b/src/item/BoatType.php
@@ -24,13 +24,11 @@
 namespace pocketmine\item;
 
 use pocketmine\block\utils\WoodType;
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static BoatType ACACIA()
  * @method static BoatType BIRCH()
@@ -40,33 +38,30 @@
  * @method static BoatType OAK()
  * @method static BoatType SPRUCE()
  */
-final class BoatType{
-	use EnumTrait {
-		__construct as Enum___construct;
-	}
+enum BoatType{
+	use LegacyEnumShimTrait;
 
-	protected static function setup() : void{
-		self::registerAll(
-			new self("oak", WoodType::OAK()),
-			new self("spruce", WoodType::SPRUCE()),
-			new self("birch", WoodType::BIRCH()),
-			new self("jungle", WoodType::JUNGLE()),
-			new self("acacia", WoodType::ACACIA()),
-			new self("dark_oak", WoodType::DARK_OAK()),
-			new self("mangrove", WoodType::MANGROVE()),
-		);
-	}
+	case OAK;
+	case SPRUCE;
+	case BIRCH;
+	case JUNGLE;
+	case ACACIA;
+	case DARK_OAK;
+	case MANGROVE;
 
-	private function __construct(
-		string $enumName,
-		private WoodType $woodType,
-	){
-		$this->Enum___construct($enumName);
+	public function getWoodType() : WoodType{
+		return match($this){
+			self::OAK => WoodType::OAK,
+			self::SPRUCE => WoodType::SPRUCE,
+			self::BIRCH => WoodType::BIRCH,
+			self::JUNGLE => WoodType::JUNGLE,
+			self::ACACIA => WoodType::ACACIA,
+			self::DARK_OAK => WoodType::DARK_OAK,
+			self::MANGROVE => WoodType::MANGROVE,
+		};
 	}
 
-	public function getWoodType() : WoodType{ return $this->woodType; }
-
 	public function getDisplayName() : string{
-		return $this->woodType->getDisplayName();
+		return $this->getWoodType()->getDisplayName();
 	}
 }
diff --git a/src/item/Bow.php b/src/item/Bow.php
index 3c1320f037e..1b25312a8e2 100644
--- a/src/item/Bow.php
+++ b/src/item/Bow.php
@@ -53,7 +53,7 @@ public function onReleaseUsing(Player $player, array &$returnedItems) : ItemUseR
 		};
 
 		if($player->hasFiniteResources() && $inventory === null){
-			return ItemUseResult::FAIL();
+			return ItemUseResult::FAIL;
 		}
 
 		$location = $player->getLocation();
@@ -95,7 +95,7 @@ public function onReleaseUsing(Player $player, array &$returnedItems) : ItemUseR
 
 		if($ev->isCancelled()){
 			$entity->flagForDespawn();
-			return ItemUseResult::FAIL();
+			return ItemUseResult::FAIL;
 		}
 
 		$entity->setMotion($entity->getMotion()->multiply($ev->getForce()));
@@ -105,7 +105,7 @@ public function onReleaseUsing(Player $player, array &$returnedItems) : ItemUseR
 			$projectileEv->call();
 			if($projectileEv->isCancelled()){
 				$ev->getProjectile()->flagForDespawn();
-				return ItemUseResult::FAIL();
+				return ItemUseResult::FAIL;
 			}
 
 			$ev->getProjectile()->spawnToAll();
@@ -121,7 +121,7 @@ public function onReleaseUsing(Player $player, array &$returnedItems) : ItemUseR
 			$this->applyDamage(1);
 		}
 
-		return ItemUseResult::SUCCESS();
+		return ItemUseResult::SUCCESS;
 	}
 
 	public function canStartUsingItem(Player $player) : bool{
diff --git a/src/item/Bucket.php b/src/item/Bucket.php
index b788a302267..ee69a0a8a5e 100644
--- a/src/item/Bucket.php
+++ b/src/item/Bucket.php
@@ -49,7 +49,7 @@ public function onInteractBlock(Player $player, Block $blockReplace, Block $bloc
 				default => null
 			};
 			if($resultItem === null){
-				return ItemUseResult::FAIL();
+				return ItemUseResult::FAIL;
 			}
 
 			$ev = new PlayerBucketFillEvent($player, $blockReplace, $face, $this, $resultItem);
@@ -60,12 +60,12 @@ public function onInteractBlock(Player $player, Block $blockReplace, Block $bloc
 
 				$this->pop();
 				$returnedItems[] = $ev->getItem();
-				return ItemUseResult::SUCCESS();
+				return ItemUseResult::SUCCESS;
 			}
 
-			return ItemUseResult::FAIL();
+			return ItemUseResult::FAIL;
 		}
 
-		return ItemUseResult::NONE();
+		return ItemUseResult::NONE;
 	}
 }
diff --git a/src/item/CoralFan.php b/src/item/CoralFan.php
index 294eb6d6e2a..7fdfc9114b9 100644
--- a/src/item/CoralFan.php
+++ b/src/item/CoralFan.php
@@ -24,7 +24,6 @@
 namespace pocketmine\item;
 
 use pocketmine\block\Block;
-use pocketmine\block\utils\CoralType;
 use pocketmine\block\utils\CoralTypeTrait;
 use pocketmine\block\VanillaBlocks;
 use pocketmine\data\runtime\RuntimeDataDescriber;
@@ -37,7 +36,6 @@ final class CoralFan extends Item{
 	}
 
 	public function __construct(ItemIdentifier $identifier){
-		$this->coralType = CoralType::TUBE();
 		parent::__construct($identifier, VanillaBlocks::CORAL_FAN()->getName());
 	}
 
diff --git a/src/item/Dye.php b/src/item/Dye.php
index f71b44e3a96..9fdfb96710b 100644
--- a/src/item/Dye.php
+++ b/src/item/Dye.php
@@ -27,15 +27,10 @@
 use pocketmine\data\runtime\RuntimeDataDescriber;
 
 class Dye extends Item{
-	private DyeColor $color;
-
-	public function __construct(ItemIdentifier $identifier, string $name){
-		$this->color = DyeColor::BLACK();
-		parent::__construct($identifier, $name);
-	}
+	private DyeColor $color = DyeColor::BLACK;
 
 	protected function describeState(RuntimeDataDescriber $w) : void{
-		$w->dyeColor($this->color);
+		$w->enum($this->color);
 	}
 
 	public function getColor() : DyeColor{
diff --git a/src/item/FireCharge.php b/src/item/FireCharge.php
index 20e7e4f89e1..a612e0897de 100644
--- a/src/item/FireCharge.php
+++ b/src/item/FireCharge.php
@@ -40,9 +40,9 @@ public function onInteractBlock(Player $player, Block $blockReplace, Block $bloc
 
 			$this->pop();
 
-			return ItemUseResult::SUCCESS();
+			return ItemUseResult::SUCCESS;
 		}
 
-		return ItemUseResult::NONE();
+		return ItemUseResult::NONE;
 	}
 }
diff --git a/src/item/FlintSteel.php b/src/item/FlintSteel.php
index 0ff2e754cd9..3e694eb0dbe 100644
--- a/src/item/FlintSteel.php
+++ b/src/item/FlintSteel.php
@@ -40,10 +40,10 @@ public function onInteractBlock(Player $player, Block $blockReplace, Block $bloc
 
 			$this->applyDamage(1);
 
-			return ItemUseResult::SUCCESS();
+			return ItemUseResult::SUCCESS;
 		}
 
-		return ItemUseResult::NONE();
+		return ItemUseResult::NONE;
 	}
 
 	public function getMaxDurability() : int{
diff --git a/src/item/GlassBottle.php b/src/item/GlassBottle.php
index bda6d132ebc..c638b109f7c 100644
--- a/src/item/GlassBottle.php
+++ b/src/item/GlassBottle.php
@@ -33,11 +33,11 @@ class GlassBottle extends Item{
 	public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{
 		if($blockClicked->getTypeId() === BlockTypeIds::WATER){
 			$this->pop();
-			$returnedItems[] = VanillaItems::POTION()->setType(PotionType::WATER());
+			$returnedItems[] = VanillaItems::POTION()->setType(PotionType::WATER);
 
-			return ItemUseResult::SUCCESS();
+			return ItemUseResult::SUCCESS;
 		}
 
-		return ItemUseResult::NONE();
+		return ItemUseResult::NONE;
 	}
 }
diff --git a/src/item/Item.php b/src/item/Item.php
index 775a82d756b..1a74345b559 100644
--- a/src/item/Item.php
+++ b/src/item/Item.php
@@ -588,7 +588,7 @@ public function getMiningEfficiency(bool $isCorrectTool) : float{
 	 * @param Item[] &$returnedItems Items to be added to the target's inventory (or dropped, if the inventory is full)
 	 */
 	public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{
-		return ItemUseResult::NONE();
+		return ItemUseResult::NONE;
 	}
 
 	/**
@@ -598,7 +598,7 @@ public function onInteractBlock(Player $player, Block $blockReplace, Block $bloc
 	 * @param Item[] &$returnedItems Items to be added to the target's inventory (or dropped, if the inventory is full)
 	 */
 	public function onClickAir(Player $player, Vector3 $directionVector, array &$returnedItems) : ItemUseResult{
-		return ItemUseResult::NONE();
+		return ItemUseResult::NONE;
 	}
 
 	/**
@@ -608,7 +608,7 @@ public function onClickAir(Player $player, Vector3 $directionVector, array &$ret
 	 * @param Item[] &$returnedItems Items to be added to the target's inventory (or dropped, if the inventory is full)
 	 */
 	public function onReleaseUsing(Player $player, array &$returnedItems) : ItemUseResult{
-		return ItemUseResult::NONE();
+		return ItemUseResult::NONE;
 	}
 
 	/**
diff --git a/src/item/ItemTypeIds.php b/src/item/ItemTypeIds.php
index 82333976661..4c2f0a1ec32 100644
--- a/src/item/ItemTypeIds.php
+++ b/src/item/ItemTypeIds.php
@@ -304,8 +304,9 @@ private function __construct(){
 	public const GLOW_BERRIES = 20265;
 	public const CHERRY_SIGN = 20266;
 	public const ENCHANTED_BOOK = 20267;
+	public const TORCHFLOWER_SEEDS = 20268;
 
-	public const FIRST_UNUSED_ITEM_ID = 20268;
+	public const FIRST_UNUSED_ITEM_ID = 20269;
 
 	private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID;
 
diff --git a/src/item/ItemUseResult.php b/src/item/ItemUseResult.php
index ff78b94e43f..e3fbe10fb4d 100644
--- a/src/item/ItemUseResult.php
+++ b/src/item/ItemUseResult.php
@@ -23,26 +23,20 @@
 
 namespace pocketmine\item;
 
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static ItemUseResult FAIL()
  * @method static ItemUseResult NONE()
  * @method static ItemUseResult SUCCESS()
  */
-final class ItemUseResult{
-	use EnumTrait;
+enum ItemUseResult{
+	use LegacyEnumShimTrait;
 
-	protected static function setup() : void{
-		self::registerAll(
-			new self("none"),
-			new self("fail"),
-			new self("success")
-		);
-	}
+	case NONE;
+	case FAIL;
+	case SUCCESS;
 }
diff --git a/src/item/LiquidBucket.php b/src/item/LiquidBucket.php
index 740f79b116e..eb2cb18ed99 100644
--- a/src/item/LiquidBucket.php
+++ b/src/item/LiquidBucket.php
@@ -56,7 +56,7 @@ public function getFuelResidue() : Item{
 
 	public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{
 		if(!$blockReplace->canBeReplaced()){
-			return ItemUseResult::NONE();
+			return ItemUseResult::NONE;
 		}
 
 		//TODO: move this to generic placement logic
@@ -70,10 +70,10 @@ public function onInteractBlock(Player $player, Block $blockReplace, Block $bloc
 
 			$this->pop();
 			$returnedItems[] = $ev->getItem();
-			return ItemUseResult::SUCCESS();
+			return ItemUseResult::SUCCESS;
 		}
 
-		return ItemUseResult::FAIL();
+		return ItemUseResult::FAIL;
 	}
 
 	public function getLiquid() : Liquid{
diff --git a/src/item/Medicine.php b/src/item/Medicine.php
index a15ac03535b..bd2f724649d 100644
--- a/src/item/Medicine.php
+++ b/src/item/Medicine.php
@@ -29,15 +29,10 @@
 
 class Medicine extends Item implements ConsumableItem{
 
-	private MedicineType $medicineType;
-
-	public function __construct(ItemIdentifier $identifier, string $name){
-		$this->medicineType = MedicineType::EYE_DROPS();
-		parent::__construct($identifier, $name);
-	}
+	private MedicineType $medicineType = MedicineType::EYE_DROPS;
 
 	protected function describeState(RuntimeDataDescriber $w) : void{
-		$w->medicineType($this->medicineType);
+		$w->enum($this->medicineType);
 	}
 
 	public function getType() : MedicineType{ return $this->medicineType; }
diff --git a/src/item/MedicineType.php b/src/item/MedicineType.php
index f7ce2b816db..ea99bb75dcf 100644
--- a/src/item/MedicineType.php
+++ b/src/item/MedicineType.php
@@ -25,42 +25,39 @@
 
 use pocketmine\entity\effect\Effect;
 use pocketmine\entity\effect\VanillaEffects;
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static MedicineType ANTIDOTE()
  * @method static MedicineType ELIXIR()
  * @method static MedicineType EYE_DROPS()
  * @method static MedicineType TONIC()
  */
-final class MedicineType{
-	use EnumTrait {
-		__construct as Enum___construct;
+enum MedicineType{
+	use LegacyEnumShimTrait;
+
+	case ANTIDOTE;
+	case ELIXIR;
+	case EYE_DROPS;
+	case TONIC;
+
+	/**
+	 * @phpstan-return array{0: string, 1: Effect}
+	 */
+	private function getMetadata() : array{
+		//cache not required here - VanillaEffects always returns the same object
+		return match($this){
+			self::ANTIDOTE => ['Antidote', VanillaEffects::POISON()],
+			self::ELIXIR => ['Elixir', VanillaEffects::WEAKNESS()],
+			self::EYE_DROPS => ['Eye Drops', VanillaEffects::BLINDNESS()],
+			self::TONIC => ['Tonic', VanillaEffects::NAUSEA()]
+		};
 	}
 
-	protected static function setup() : void{
-		self::registerAll(
-			new self('antidote', 'Antidote', VanillaEffects::POISON()),
-			new self('elixir', 'Elixir', VanillaEffects::WEAKNESS()),
-			new self('eye_drops', 'Eye Drops', VanillaEffects::BLINDNESS()),
-			new self('tonic', 'Tonic', VanillaEffects::NAUSEA())
-		);
-	}
-
-	private function __construct(
-		string $enumName,
-		private string $displayName,
-		private Effect $curedEffect
-	){
-		$this->Enum___construct($enumName);
-	}
-
-	public function getDisplayName() : string{ return $this->displayName; }
+	public function getDisplayName() : string{ return $this->getMetadata()[0]; }
 
-	public function getCuredEffect() : Effect{ return $this->curedEffect; }
+	public function getCuredEffect() : Effect{ return $this->getMetadata()[1]; }
 }
diff --git a/src/item/PaintingItem.php b/src/item/PaintingItem.php
index f3821d0b26a..a83c8dba8fe 100644
--- a/src/item/PaintingItem.php
+++ b/src/item/PaintingItem.php
@@ -39,7 +39,7 @@ class PaintingItem extends Item{
 
 	public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{
 		if(Facing::axis($face) === Axis::Y){
-			return ItemUseResult::NONE();
+			return ItemUseResult::NONE;
 		}
 
 		$motives = [];
@@ -67,7 +67,7 @@ public function onInteractBlock(Player $player, Block $blockReplace, Block $bloc
 		}
 
 		if(count($motives) === 0){ //No space available
-			return ItemUseResult::NONE();
+			return ItemUseResult::NONE;
 		}
 
 		/** @var PaintingMotive $motive */
@@ -81,6 +81,6 @@ public function onInteractBlock(Player $player, Block $blockReplace, Block $bloc
 		$entity->spawnToAll();
 
 		$player->getWorld()->addSound($replacePos->add(0.5, 0.5, 0.5), new PaintingPlaceSound());
-		return ItemUseResult::SUCCESS();
+		return ItemUseResult::SUCCESS;
 	}
 }
diff --git a/src/item/Potion.php b/src/item/Potion.php
index 0ef3398666e..41b0f634a88 100644
--- a/src/item/Potion.php
+++ b/src/item/Potion.php
@@ -29,15 +29,10 @@
 
 class Potion extends Item implements ConsumableItem{
 
-	private PotionType $potionType;
-
-	public function __construct(ItemIdentifier $identifier, string $name){
-		$this->potionType = PotionType::WATER();
-		parent::__construct($identifier, $name);
-	}
+	private PotionType $potionType = PotionType::WATER;
 
 	protected function describeState(RuntimeDataDescriber $w) : void{
-		$w->potionType($this->potionType);
+		$w->enum($this->potionType);
 	}
 
 	public function getType() : PotionType{ return $this->potionType; }
diff --git a/src/item/PotionType.php b/src/item/PotionType.php
index e7feb0b8ec9..3f4773e6de5 100644
--- a/src/item/PotionType.php
+++ b/src/item/PotionType.php
@@ -25,13 +25,12 @@
 
 use pocketmine\entity\effect\EffectInstance;
 use pocketmine\entity\effect\VanillaEffects;
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
+use function spl_object_id;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static PotionType AWKWARD()
  * @method static PotionType FIRE_RESISTANCE()
@@ -76,157 +75,196 @@
  * @method static PotionType WATER_BREATHING()
  * @method static PotionType WEAKNESS()
  * @method static PotionType WITHER()
+ *
+ * @phpstan-type TMetadata array{0: string, 1: \Closure() : list<EffectInstance>}
  */
-final class PotionType{
-	use EnumTrait {
-		__construct as Enum___construct;
-	}
+enum PotionType{
+	use LegacyEnumShimTrait;
+
+	case WATER;
+	case MUNDANE;
+	case LONG_MUNDANE;
+	case THICK;
+	case AWKWARD;
+	case NIGHT_VISION;
+	case LONG_NIGHT_VISION;
+	case INVISIBILITY;
+	case LONG_INVISIBILITY;
+	case LEAPING;
+	case LONG_LEAPING;
+	case STRONG_LEAPING;
+	case FIRE_RESISTANCE;
+	case LONG_FIRE_RESISTANCE;
+	case SWIFTNESS;
+	case LONG_SWIFTNESS;
+	case STRONG_SWIFTNESS;
+	case SLOWNESS;
+	case LONG_SLOWNESS;
+	case WATER_BREATHING;
+	case LONG_WATER_BREATHING;
+	case HEALING;
+	case STRONG_HEALING;
+	case HARMING;
+	case STRONG_HARMING;
+	case POISON;
+	case LONG_POISON;
+	case STRONG_POISON;
+	case REGENERATION;
+	case LONG_REGENERATION;
+	case STRONG_REGENERATION;
+	case STRENGTH;
+	case LONG_STRENGTH;
+	case STRONG_STRENGTH;
+	case WEAKNESS;
+	case LONG_WEAKNESS;
+	case WITHER;
+	case TURTLE_MASTER;
+	case LONG_TURTLE_MASTER;
+	case STRONG_TURTLE_MASTER;
+	case SLOW_FALLING;
+	case LONG_SLOW_FALLING;
+	case STRONG_SLOWNESS;
+
+	/**
+	 * @phpstan-return TMetadata
+	 */
+	private function getMetadata() : array{
+		/** @phpstan-var array<int, TMetadata> $cache */
+		static $cache = [];
 
-	protected static function setup() : void{
-		self::registerAll(
-			new self("water", "Water", fn() => []),
-			new self("mundane", "Mundane", fn() => []),
-			new self("long_mundane", "Long Mundane", fn() => []),
-			new self("thick", "Thick", fn() => []),
-			new self("awkward", "Awkward", fn() => []),
-			new self("night_vision", "Night Vision", fn() => [
+		return $cache[spl_object_id($this)] ??= match($this){
+			self::WATER => ["Water", fn() => []],
+			self::MUNDANE => ["Mundane", fn() => []],
+			self::LONG_MUNDANE => ["Long Mundane", fn() => []],
+			self::THICK => ["Thick", fn() => []],
+			self::AWKWARD => ["Awkward", fn() => []],
+			self::NIGHT_VISION => ["Night Vision", fn() => [
 				new EffectInstance(VanillaEffects::NIGHT_VISION(), 3600)
-			]),
-			new self("long_night_vision", "Long Night Vision", fn() => [
+			]],
+			self::LONG_NIGHT_VISION => ["Long Night Vision", fn() => [
 				new EffectInstance(VanillaEffects::NIGHT_VISION(), 9600)
-			]),
-			new self("invisibility", "Invisibility", fn() => [
+			]],
+			self::INVISIBILITY => ["Invisibility", fn() => [
 				new EffectInstance(VanillaEffects::INVISIBILITY(), 3600)
-			]),
-			new self("long_invisibility", "Long Invisibility", fn() => [
+			]],
+			self::LONG_INVISIBILITY => ["Long Invisibility", fn() => [
 				new EffectInstance(VanillaEffects::INVISIBILITY(), 9600)
-			]),
-			new self("leaping", "Leaping", fn() => [
+			]],
+			self::LEAPING => ["Leaping", fn() => [
 				new EffectInstance(VanillaEffects::JUMP_BOOST(), 3600)
-			]),
-			new self("long_leaping", "Long Leaping", fn() => [
+			]],
+			self::LONG_LEAPING => ["Long Leaping", fn() => [
 				new EffectInstance(VanillaEffects::JUMP_BOOST(), 9600)
-			]),
-			new self("strong_leaping", "Strong Leaping", fn() => [
+			]],
+			self::STRONG_LEAPING => ["Strong Leaping", fn() => [
 				new EffectInstance(VanillaEffects::JUMP_BOOST(), 1800, 1)
-			]),
-			new self("fire_resistance", "Fire Resistance", fn() => [
+			]],
+			self::FIRE_RESISTANCE => ["Fire Resistance", fn() => [
 				new EffectInstance(VanillaEffects::FIRE_RESISTANCE(), 3600)
-			]),
-			new self("long_fire_resistance", "Long Fire Resistance", fn() => [
+			]],
+			self::LONG_FIRE_RESISTANCE => ["Long Fire Resistance", fn() => [
 				new EffectInstance(VanillaEffects::FIRE_RESISTANCE(), 9600)
-			]),
-			new self("swiftness", "Swiftness", fn() => [
+			]],
+			self::SWIFTNESS => ["Swiftness", fn() => [
 				new EffectInstance(VanillaEffects::SPEED(), 3600)
-			]),
-			new self("long_swiftness", "Long Swiftness", fn() => [
+			]],
+			self::LONG_SWIFTNESS => ["Long Swiftness", fn() => [
 				new EffectInstance(VanillaEffects::SPEED(), 9600)
-			]),
-			new self("strong_swiftness", "Strong Swiftness", fn() => [
+			]],
+			self::STRONG_SWIFTNESS => ["Strong Swiftness", fn() => [
 				new EffectInstance(VanillaEffects::SPEED(), 1800, 1)
-			]),
-			new self("slowness", "Slowness", fn() => [
+			]],
+			self::SLOWNESS => ["Slowness", fn() => [
 				new EffectInstance(VanillaEffects::SLOWNESS(), 1800)
-			]),
-			new self("long_slowness", "Long Slowness", fn() => [
+			]],
+			self::LONG_SLOWNESS => ["Long Slowness", fn() => [
 				new EffectInstance(VanillaEffects::SLOWNESS(), 4800)
-			]),
-			new self("water_breathing", "Water Breathing", fn() => [
+			]],
+			self::WATER_BREATHING => ["Water Breathing", fn() => [
 				new EffectInstance(VanillaEffects::WATER_BREATHING(), 3600)
-			]),
-			new self("long_water_breathing", "Long Water Breathing", fn() => [
+			]],
+			self::LONG_WATER_BREATHING => ["Long Water Breathing", fn() => [
 				new EffectInstance(VanillaEffects::WATER_BREATHING(), 9600)
-			]),
-			new self("healing", "Healing", fn() => [
+			]],
+			self::HEALING => ["Healing", fn() => [
 				new EffectInstance(VanillaEffects::INSTANT_HEALTH())
-			]),
-			new self("strong_healing", "Strong Healing", fn() => [
+			]],
+			self::STRONG_HEALING => ["Strong Healing", fn() => [
 				new EffectInstance(VanillaEffects::INSTANT_HEALTH(), null, 1)
-			]),
-			new self("harming", "Harming", fn() => [
+			]],
+			self::HARMING => ["Harming", fn() => [
 				new EffectInstance(VanillaEffects::INSTANT_DAMAGE())
-			]),
-			new self("strong_harming", "Strong Harming", fn() => [
+			]],
+			self::STRONG_HARMING => ["Strong Harming", fn() => [
 				new EffectInstance(VanillaEffects::INSTANT_DAMAGE(), null, 1)
-			]),
-			new self("poison", "Poison", fn() => [
+			]],
+			self::POISON => ["Poison", fn() => [
 				new EffectInstance(VanillaEffects::POISON(), 900)
-			]),
-			new self("long_poison", "Long Poison", fn() => [
+			]],
+			self::LONG_POISON => ["Long Poison", fn() => [
 				new EffectInstance(VanillaEffects::POISON(), 2400)
-			]),
-			new self("strong_poison", "Strong Poison", fn() => [
+			]],
+			self::STRONG_POISON => ["Strong Poison", fn() => [
 				new EffectInstance(VanillaEffects::POISON(), 440, 1)
-			]),
-			new self("regeneration", "Regeneration", fn() => [
+			]],
+			self::REGENERATION => ["Regeneration", fn() => [
 				new EffectInstance(VanillaEffects::REGENERATION(), 900)
-			]),
-			new self("long_regeneration", "Long Regeneration", fn() => [
+			]],
+			self::LONG_REGENERATION => ["Long Regeneration", fn() => [
 				new EffectInstance(VanillaEffects::REGENERATION(), 2400)
-			]),
-			new self("strong_regeneration", "Strong Regeneration", fn() => [
+			]],
+			self::STRONG_REGENERATION => ["Strong Regeneration", fn() => [
 				new EffectInstance(VanillaEffects::REGENERATION(), 440, 1)
-			]),
-			new self("strength", "Strength", fn() => [
+			]],
+			self::STRENGTH => ["Strength", fn() => [
 				new EffectInstance(VanillaEffects::STRENGTH(), 3600)
-			]),
-			new self("long_strength", "Long Strength", fn() => [
+			]],
+			self::LONG_STRENGTH => ["Long Strength", fn() => [
 				new EffectInstance(VanillaEffects::STRENGTH(), 9600)
-			]),
-			new self("strong_strength", "Strong Strength", fn() => [
+			]],
+			self::STRONG_STRENGTH => ["Strong Strength", fn() => [
 				new EffectInstance(VanillaEffects::STRENGTH(), 1800, 1)
-			]),
-			new self("weakness", "Weakness", fn() => [
+			]],
+			self::WEAKNESS => ["Weakness", fn() => [
 				new EffectInstance(VanillaEffects::WEAKNESS(), 1800)
-			]),
-			new self("long_weakness", "Long Weakness", fn() => [
+			]],
+			self::LONG_WEAKNESS => ["Long Weakness", fn() => [
 				new EffectInstance(VanillaEffects::WEAKNESS(), 4800)
-			]),
-			new self("wither", "Wither", fn() => [
+			]],
+			self::WITHER => ["Wither", fn() => [
 				new EffectInstance(VanillaEffects::WITHER(), 800, 1)
-			]),
-			new self("turtle_master", "Turtle Master", fn() => [
+			]],
+			self::TURTLE_MASTER => ["Turtle Master", fn() => [
 				new EffectInstance(VanillaEffects::SLOWNESS(), 20 * 20, 3),
 				new EffectInstance(VanillaEffects::RESISTANCE(), 20 * 20, 2),
-			]),
-			new self("long_turtle_master", "Long Turtle Master", fn() => [
+			]],
+			self::LONG_TURTLE_MASTER => ["Long Turtle Master", fn() => [
 				new EffectInstance(VanillaEffects::SLOWNESS(), 40 * 20, 3),
 				new EffectInstance(VanillaEffects::RESISTANCE(), 40 * 20, 2),
-			]),
-			new self("strong_turtle_master", "Strong Turtle Master", fn() => [
+			]],
+			self::STRONG_TURTLE_MASTER => ["Strong Turtle Master", fn() => [
 				new EffectInstance(VanillaEffects::SLOWNESS(), 20 * 20, 5),
 				new EffectInstance(VanillaEffects::RESISTANCE(), 20 * 20, 3),
-			]),
-			new self("slow_falling", "Slow Falling", fn() => [
+			]],
+			self::SLOW_FALLING => ["Slow Falling", fn() => [
 				//TODO
-			]),
-			new self("long_slow_falling", "Long Slow Falling", fn() => [
+			]],
+			self::LONG_SLOW_FALLING => ["Long Slow Falling", fn() => [
 				//TODO
-			]),
-			new self("strong_slowness", "Strong Slowness", fn() => [
+			]],
+			self::STRONG_SLOWNESS => ["Strong Slowness", fn() => [
 				new EffectInstance(VanillaEffects::SLOWNESS(), 20 * 20, 3)
-			])
-		);
-	}
-
-	/**
-	 * @phpstan-param \Closure() : list<EffectInstance> $effectsGetter
-	 */
-	private function __construct(
-		string $enumName,
-		private string $displayName,
-		private \Closure $effectsGetter
-	){
-		$this->Enum___construct($enumName);
+			]]
+		};
 	}
 
-	public function getDisplayName() : string{ return $this->displayName; }
+	public function getDisplayName() : string{ return $this->getMetadata()[0]; }
 
 	/**
 	 * @return EffectInstance[]
 	 * @phpstan-return list<EffectInstance>
 	 */
 	public function getEffects() : array{
-		return ($this->effectsGetter)();
+		return ($this->getMetadata()[1])();
 	}
 }
diff --git a/src/item/ProjectileItem.php b/src/item/ProjectileItem.php
index 8056af505a5..47c4583e046 100644
--- a/src/item/ProjectileItem.php
+++ b/src/item/ProjectileItem.php
@@ -46,7 +46,7 @@ public function onClickAir(Player $player, Vector3 $directionVector, array &$ret
 		$projectileEv->call();
 		if($projectileEv->isCancelled()){
 			$projectile->flagForDespawn();
-			return ItemUseResult::FAIL();
+			return ItemUseResult::FAIL;
 		}
 
 		$projectile->spawnToAll();
@@ -55,6 +55,6 @@ public function onClickAir(Player $player, Vector3 $directionVector, array &$ret
 
 		$this->pop();
 
-		return ItemUseResult::SUCCESS();
+		return ItemUseResult::SUCCESS;
 	}
 }
diff --git a/src/item/SpawnEgg.php b/src/item/SpawnEgg.php
index 1147a951ddb..51dcceebd2a 100644
--- a/src/item/SpawnEgg.php
+++ b/src/item/SpawnEgg.php
@@ -43,6 +43,6 @@ public function onInteractBlock(Player $player, Block $blockReplace, Block $bloc
 		$this->pop();
 		$entity->spawnToAll();
 		//TODO: what if the entity was marked for deletion?
-		return ItemUseResult::SUCCESS();
+		return ItemUseResult::SUCCESS;
 	}
 }
diff --git a/src/item/SplashPotion.php b/src/item/SplashPotion.php
index c54562f2b05..e1c9167ac34 100644
--- a/src/item/SplashPotion.php
+++ b/src/item/SplashPotion.php
@@ -31,15 +31,10 @@
 
 class SplashPotion extends ProjectileItem{
 
-	private PotionType $potionType;
-
-	public function __construct(ItemIdentifier $identifier, string $name){
-		$this->potionType = PotionType::WATER();
-		parent::__construct($identifier, $name);
-	}
+	private PotionType $potionType = PotionType::WATER;
 
 	protected function describeState(RuntimeDataDescriber $w) : void{
-		$w->potionType($this->potionType);
+		$w->enum($this->potionType);
 	}
 
 	public function getType() : PotionType{ return $this->potionType; }
diff --git a/src/item/StringToItemParser.php b/src/item/StringToItemParser.php
index d482e4bef9a..2e05ddc6ef6 100644
--- a/src/item/StringToItemParser.php
+++ b/src/item/StringToItemParser.php
@@ -23,6 +23,7 @@
 
 namespace pocketmine\item;
 
+use pocketmine\block\AmethystCluster;
 use pocketmine\block\Block;
 use pocketmine\block\Light;
 use pocketmine\block\utils\CopperOxidation;
@@ -37,6 +38,7 @@
 use pocketmine\utils\SingletonTrait;
 use pocketmine\utils\StringToTParser;
 use function array_keys;
+use function strtolower;
 
 /**
  * Handles parsing items from strings. This is used to interpret names from the /give command (and others).
@@ -58,8 +60,8 @@ private static function make() : self{
 	}
 
 	private static function registerDynamicBlocks(self $result) : void{
-		foreach(DyeColor::getAll() as $color){
-			$register = fn(string $name, \Closure $callback) => $result->registerBlock($color->name() . "_" . $name, $callback);
+		foreach(DyeColor::cases() as $color){
+			$register = fn(string $name, \Closure $callback) => $result->registerBlock(strtolower($color->name) . "_" . $name, $callback);
 			//wall and floor banner are the same item
 			$register("banner", fn() => Blocks::BANNER()->setColor($color));
 			$register("bed", fn() => Blocks::BED()->setColor($color));
@@ -77,8 +79,8 @@ private static function registerDynamicBlocks(self $result) : void{
 			$register("shulker_box", fn() => Blocks::DYED_SHULKER_BOX()->setColor($color));
 		}
 
-		foreach(CoralType::getAll() as $coralType){
-			$register = fn(string $name, \Closure $callback) => $result->registerBlock($coralType->name() . "_" . $name, $callback);
+		foreach(CoralType::cases() as $coralType){
+			$register = fn(string $name, \Closure $callback) => $result->registerBlock(strtolower($coralType->name) . "_" . $name, $callback);
 			$register("coral", fn() => Blocks::CORAL()->setCoralType($coralType));
 			$register("coral_block", fn() => Blocks::CORAL_BLOCK()->setCoralType($coralType));
 			//wall and floor coral fans are the same item
@@ -90,8 +92,8 @@ private static function registerDynamicBlocks(self $result) : void{
 			$result->registerBlock("light_block_$i", fn() => Blocks::LIGHT()->setLightLevel($i));
 		}
 
-		foreach(CopperOxidation::getAll() as $oxidation){
-			$oxPrefix = $oxidation->equals(CopperOxidation::NONE()) ? "" : $oxidation->name() . "_";
+		foreach(CopperOxidation::cases() as $oxidation){
+			$oxPrefix = $oxidation === CopperOxidation::NONE ? "" : strtolower($oxidation->name) . "_";
 
 			foreach(["" => false, "waxed_" => true] as $waxedPrefix => $waxed){
 				$register = fn(string $name, \Closure $callback) => $result->registerBlock($waxedPrefix . $oxPrefix . $name, $callback);
@@ -102,8 +104,8 @@ private static function registerDynamicBlocks(self $result) : void{
 			}
 		}
 
-		foreach(FroglightType::getAll() as $froglightType){
-			$result->registerBlock($froglightType->name() . "_froglight", fn() => Blocks::FROGLIGHT()->setFroglightType($froglightType));
+		foreach(FroglightType::cases() as $froglightType){
+			$result->registerBlock(strtolower($froglightType->name) . "_froglight", fn() => Blocks::FROGLIGHT()->setFroglightType($froglightType));
 		}
 	}
 
@@ -133,6 +135,7 @@ private static function registerBlocks(self $result) : void{
 		$result->registerBlock("all_sided_mushroom_stem", fn() => Blocks::ALL_SIDED_MUSHROOM_STEM());
 		$result->registerBlock("allium", fn() => Blocks::ALLIUM());
 		$result->registerBlock("amethyst_block", fn() => Blocks::AMETHYST());
+		$result->registerBlock("amethyst_cluster", fn() => Blocks::AMETHYST_CLUSTER());
 		$result->registerBlock("ancient_debris", fn() => Blocks::ANCIENT_DEBRIS());
 		$result->registerBlock("andesite", fn() => Blocks::ANDESITE());
 		$result->registerBlock("andesite_slab", fn() => Blocks::ANDESITE_SLAB());
@@ -195,6 +198,7 @@ private static function registerBlocks(self $result) : void{
 		$result->registerBlock("bricks_block", fn() => Blocks::BRICKS());
 		$result->registerBlock("brown_mushroom", fn() => Blocks::BROWN_MUSHROOM());
 		$result->registerBlock("brown_mushroom_block", fn() => Blocks::BROWN_MUSHROOM_BLOCK());
+		$result->registerBlock("budding_amethyst", fn() => Blocks::BUDDING_AMETHYST());
 		$result->registerBlock("burning_furnace", fn() => Blocks::FURNACE());
 		$result->registerBlock("bush", fn() => Blocks::DEAD_BUSH());
 		$result->registerBlock("cactus", fn() => Blocks::CACTUS());
@@ -226,6 +230,7 @@ private static function registerBlocks(self $result) : void{
 		$result->registerBlock("chemistry_table", fn() => Blocks::COMPOUND_CREATOR());
 		$result->registerBlock("chest", fn() => Blocks::CHEST());
 		$result->registerBlock("chipped_anvil", fn() => Blocks::ANVIL()->setDamage(1));
+		$result->registerBlock("chiseled_bookshelf", fn() => Blocks::CHISELED_BOOKSHELF());
 		$result->registerBlock("chiseled_deepslate", fn() => Blocks::CHISELED_DEEPSLATE());
 		$result->registerBlock("chiseled_nether_bricks", fn() => Blocks::CHISELED_NETHER_BRICKS());
 		$result->registerBlock("chiseled_polished_blackstone", fn() => Blocks::CHISELED_POLISHED_BLACKSTONE());
@@ -238,7 +243,7 @@ private static function registerBlocks(self $result) : void{
 		$result->registerBlock("clay_block", fn() => Blocks::CLAY());
 		$result->registerBlock("coal_block", fn() => Blocks::COAL());
 		$result->registerBlock("coal_ore", fn() => Blocks::COAL_ORE());
-		$result->registerBlock("coarse_dirt", fn() => Blocks::DIRT()->setDirtType(DirtType::COARSE()));
+		$result->registerBlock("coarse_dirt", fn() => Blocks::DIRT()->setDirtType(DirtType::COARSE));
 		$result->registerBlock("cobble", fn() => Blocks::COBBLESTONE());
 		$result->registerBlock("cobble_stairs", fn() => Blocks::COBBLESTONE_STAIRS());
 		$result->registerBlock("cobble_wall", fn() => Blocks::COBBLESTONE_WALL());
@@ -267,10 +272,10 @@ private static function registerBlocks(self $result) : void{
 		$result->registerBlock("coral", fn() => Blocks::CORAL());
 		$result->registerBlock("coral_block", fn() => Blocks::CORAL_BLOCK());
 		$result->registerBlock("coral_fan", fn() => Blocks::CORAL_FAN());
-		$result->registerBlock("coral_fan_dead", fn() => Blocks::CORAL_FAN()->setCoralType(CoralType::TUBE())->setDead(true));
+		$result->registerBlock("coral_fan_dead", fn() => Blocks::CORAL_FAN()->setCoralType(CoralType::TUBE)->setDead(true));
 		$result->registerBlock("coral_fan_hang", fn() => Blocks::WALL_CORAL_FAN());
-		$result->registerBlock("coral_fan_hang2", fn() => Blocks::WALL_CORAL_FAN()->setCoralType(CoralType::BUBBLE()));
-		$result->registerBlock("coral_fan_hang3", fn() => Blocks::WALL_CORAL_FAN()->setCoralType(CoralType::HORN()));
+		$result->registerBlock("coral_fan_hang2", fn() => Blocks::WALL_CORAL_FAN()->setCoralType(CoralType::BUBBLE));
+		$result->registerBlock("coral_fan_hang3", fn() => Blocks::WALL_CORAL_FAN()->setCoralType(CoralType::HORN));
 		$result->registerBlock("cornflower", fn() => Blocks::CORNFLOWER());
 		$result->registerBlock("cracked_deepslate_bricks", fn() => Blocks::CRACKED_DEEPSLATE_BRICKS());
 		$result->registerBlock("cracked_deepslate_tiles", fn() => Blocks::CRACKED_DEEPSLATE_TILES());
@@ -278,7 +283,7 @@ private static function registerBlocks(self $result) : void{
 		$result->registerBlock("cracked_polished_blackstone_bricks", fn() => Blocks::CRACKED_POLISHED_BLACKSTONE_BRICKS());
 		$result->registerBlock("cracked_stone_bricks", fn() => Blocks::CRACKED_STONE_BRICKS());
 		$result->registerBlock("crafting_table", fn() => Blocks::CRAFTING_TABLE());
-		$result->registerBlock("creeper_head", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::CREEPER()));
+		$result->registerBlock("creeper_head", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::CREEPER));
 		$result->registerBlock("crimson_button", fn() => Blocks::CRIMSON_BUTTON());
 		$result->registerBlock("crimson_door", fn() => Blocks::CRIMSON_DOOR());
 		$result->registerBlock("crimson_fence", fn() => Blocks::CRIMSON_FENCE());
@@ -286,6 +291,7 @@ private static function registerBlocks(self $result) : void{
 		$result->registerBlock("crimson_hyphae", fn() => Blocks::CRIMSON_HYPHAE()->setStripped(false));
 		$result->registerBlock("crimson_planks", fn() => Blocks::CRIMSON_PLANKS());
 		$result->registerBlock("crimson_pressure_plate", fn() => Blocks::CRIMSON_PRESSURE_PLATE());
+		$result->registerBlock("crimson_roots", fn() => Blocks::CRIMSON_ROOTS());
 		$result->registerBlock("crimson_sign", fn() => Blocks::CRIMSON_SIGN());
 		$result->registerBlock("crimson_slab", fn() => Blocks::CRIMSON_SLAB());
 		$result->registerBlock("crimson_stairs", fn() => Blocks::CRIMSON_STAIRS());
@@ -353,24 +359,24 @@ private static function registerBlocks(self $result) : void{
 		$result->registerBlock("diorite_slab", fn() => Blocks::DIORITE_SLAB());
 		$result->registerBlock("diorite_stairs", fn() => Blocks::DIORITE_STAIRS());
 		$result->registerBlock("diorite_wall", fn() => Blocks::DIORITE_WALL());
-		$result->registerBlock("dirt", fn() => Blocks::DIRT()->setDirtType(DirtType::NORMAL()));
-		$result->registerBlock("dirt_with_roots", fn() => Blocks::DIRT()->setDirtType(DirtType::ROOTED()));
+		$result->registerBlock("dirt", fn() => Blocks::DIRT()->setDirtType(DirtType::NORMAL));
+		$result->registerBlock("dirt_with_roots", fn() => Blocks::DIRT()->setDirtType(DirtType::ROOTED));
 		$result->registerBlock("door_block", fn() => Blocks::OAK_DOOR());
 		$result->registerBlock("double_plant", fn() => Blocks::SUNFLOWER());
-		$result->registerBlock("double_red_sandstone_slab", fn() => Blocks::RED_SANDSTONE_SLAB()->setSlabType(SlabType::DOUBLE()));
-		$result->registerBlock("double_slab", fn() => Blocks::STONE_SLAB()->setSlabType(SlabType::DOUBLE()));
-		$result->registerBlock("double_slabs", fn() => Blocks::STONE_SLAB()->setSlabType(SlabType::DOUBLE()));
-		$result->registerBlock("double_stone_slab", fn() => Blocks::STONE_SLAB()->setSlabType(SlabType::DOUBLE()));
-		$result->registerBlock("double_stone_slab2", fn() => Blocks::RED_SANDSTONE_SLAB()->setSlabType(SlabType::DOUBLE()));
-		$result->registerBlock("double_stone_slab3", fn() => Blocks::END_STONE_BRICK_SLAB()->setSlabType(SlabType::DOUBLE()));
-		$result->registerBlock("double_stone_slab4", fn() => Blocks::MOSSY_STONE_BRICK_SLAB()->setSlabType(SlabType::DOUBLE()));
+		$result->registerBlock("double_red_sandstone_slab", fn() => Blocks::RED_SANDSTONE_SLAB()->setSlabType(SlabType::DOUBLE));
+		$result->registerBlock("double_slab", fn() => Blocks::STONE_SLAB()->setSlabType(SlabType::DOUBLE));
+		$result->registerBlock("double_slabs", fn() => Blocks::STONE_SLAB()->setSlabType(SlabType::DOUBLE));
+		$result->registerBlock("double_stone_slab", fn() => Blocks::STONE_SLAB()->setSlabType(SlabType::DOUBLE));
+		$result->registerBlock("double_stone_slab2", fn() => Blocks::RED_SANDSTONE_SLAB()->setSlabType(SlabType::DOUBLE));
+		$result->registerBlock("double_stone_slab3", fn() => Blocks::END_STONE_BRICK_SLAB()->setSlabType(SlabType::DOUBLE));
+		$result->registerBlock("double_stone_slab4", fn() => Blocks::MOSSY_STONE_BRICK_SLAB()->setSlabType(SlabType::DOUBLE));
 		$result->registerBlock("double_tallgrass", fn() => Blocks::DOUBLE_TALLGRASS());
-		$result->registerBlock("double_wood_slab", fn() => Blocks::OAK_SLAB()->setSlabType(SlabType::DOUBLE()));
-		$result->registerBlock("double_wood_slabs", fn() => Blocks::OAK_SLAB()->setSlabType(SlabType::DOUBLE()));
-		$result->registerBlock("double_wooden_slab", fn() => Blocks::OAK_SLAB()->setSlabType(SlabType::DOUBLE()));
-		$result->registerBlock("double_wooden_slabs", fn() => Blocks::OAK_SLAB()->setSlabType(SlabType::DOUBLE()));
+		$result->registerBlock("double_wood_slab", fn() => Blocks::OAK_SLAB()->setSlabType(SlabType::DOUBLE));
+		$result->registerBlock("double_wood_slabs", fn() => Blocks::OAK_SLAB()->setSlabType(SlabType::DOUBLE));
+		$result->registerBlock("double_wooden_slab", fn() => Blocks::OAK_SLAB()->setSlabType(SlabType::DOUBLE));
+		$result->registerBlock("double_wooden_slabs", fn() => Blocks::OAK_SLAB()->setSlabType(SlabType::DOUBLE));
 		$result->registerBlock("dragon_egg", fn() => Blocks::DRAGON_EGG());
-		$result->registerBlock("dragon_head", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::DRAGON()));
+		$result->registerBlock("dragon_head", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::DRAGON));
 		$result->registerBlock("dried_kelp_block", fn() => Blocks::DRIED_KELP());
 		$result->registerBlock("dyed_shulker_box", fn() => Blocks::DYED_SHULKER_BOX());
 		$result->registerBlock("element_0", fn() => Blocks::ELEMENT_ZERO());
@@ -742,6 +748,7 @@ private static function registerBlocks(self $result) : void{
 		$result->registerBlock("lapis_lazuli_block", fn() => Blocks::LAPIS_LAZULI());
 		$result->registerBlock("lapis_lazuli_ore", fn() => Blocks::LAPIS_LAZULI_ORE());
 		$result->registerBlock("lapis_ore", fn() => Blocks::LAPIS_LAZULI_ORE());
+		$result->registerBlock("large_amethyst_bud", fn() => Blocks::AMETHYST_CLUSTER()->setStage(AmethystCluster::STAGE_LARGE_BUD));
 		$result->registerBlock("large_fern", fn() => Blocks::LARGE_FERN());
 		$result->registerBlock("lava", fn() => Blocks::LAVA());
 		$result->registerBlock("leave", fn() => Blocks::OAK_LEAVES());
@@ -784,6 +791,7 @@ private static function registerBlocks(self $result) : void{
 		$result->registerBlock("mangrove_trapdoor", fn() => Blocks::MANGROVE_TRAPDOOR());
 		$result->registerBlock("mangrove_wood", fn() => Blocks::MANGROVE_WOOD()->setStripped(false));
 		$result->registerBlock("material_reducer", fn() => Blocks::MATERIAL_REDUCER());
+		$result->registerBlock("medium_amethyst_bud", fn() => Blocks::AMETHYST_CLUSTER()->setStage(AmethystCluster::STAGE_MEDIUM_BUD));
 		$result->registerBlock("melon_block", fn() => Blocks::MELON());
 		$result->registerBlock("melon_stem", fn() => Blocks::MELON_STEM());
 		$result->registerBlock("mob_head", fn() => Blocks::MOB_HEAD());
@@ -858,10 +866,10 @@ private static function registerBlocks(self $result) : void{
 		$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("piglin_head", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::PIGLIN));
 		$result->registerBlock("plank", fn() => Blocks::OAK_PLANKS());
 		$result->registerBlock("planks", fn() => Blocks::OAK_PLANKS());
-		$result->registerBlock("player_head", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::PLAYER()));
+		$result->registerBlock("player_head", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::PLAYER));
 		$result->registerBlock("podzol", fn() => Blocks::PODZOL());
 		$result->registerBlock("polished_andesite", fn() => Blocks::POLISHED_ANDESITE());
 		$result->registerBlock("polished_andesite_slab", fn() => Blocks::POLISHED_ANDESITE_SLAB());
@@ -950,7 +958,7 @@ private static function registerBlocks(self $result) : void{
 		$result->registerBlock("repeater", fn() => Blocks::REDSTONE_REPEATER());
 		$result->registerBlock("repeater_block", fn() => Blocks::REDSTONE_REPEATER());
 		$result->registerBlock("reserved6", fn() => Blocks::RESERVED6());
-		$result->registerBlock("rooted_dirt", fn() => Blocks::DIRT()->setDirtType(DirtType::ROOTED()));
+		$result->registerBlock("rooted_dirt", fn() => Blocks::DIRT()->setDirtType(DirtType::ROOTED));
 		$result->registerBlock("rose", fn() => Blocks::POPPY());
 		$result->registerBlock("rose_bush", fn() => Blocks::ROSE_BUSH());
 		$result->registerBlock("sand", fn() => Blocks::SAND());
@@ -967,13 +975,14 @@ private static function registerBlocks(self $result) : void{
 		$result->registerBlock("shulker_box", fn() => Blocks::SHULKER_BOX());
 		$result->registerBlock("sign", fn() => Blocks::OAK_SIGN());
 		$result->registerBlock("sign_post", fn() => Blocks::OAK_SIGN());
-		$result->registerBlock("skeleton_skull", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::SKELETON()));
-		$result->registerBlock("skull", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::SKELETON()));
+		$result->registerBlock("skeleton_skull", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::SKELETON));
+		$result->registerBlock("skull", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::SKELETON));
 		$result->registerBlock("skull_block", fn() => Blocks::MOB_HEAD());
 		$result->registerBlock("slab", fn() => Blocks::SMOOTH_STONE_SLAB());
 		$result->registerBlock("slabs", fn() => Blocks::SMOOTH_STONE_SLAB());
 		$result->registerBlock("slime", fn() => Blocks::SLIME());
 		$result->registerBlock("slime_block", fn() => Blocks::SLIME());
+		$result->registerBlock("small_amethyst_bud", fn() => Blocks::AMETHYST_CLUSTER()->setStage(AmethystCluster::STAGE_SMALL_BUD));
 		$result->registerBlock("small_dripleaf", fn() => Blocks::SMALL_DRIPLEAF());
 		$result->registerBlock("smoker", fn() => Blocks::SMOKER());
 		$result->registerBlock("smooth_basalt", fn() => Blocks::SMOOTH_BASALT());
@@ -1073,6 +1082,7 @@ private static function registerBlocks(self $result) : void{
 		$result->registerBlock("tinted_glass", fn() => Blocks::TINTED_GLASS());
 		$result->registerBlock("tnt", fn() => Blocks::TNT());
 		$result->registerBlock("torch", fn() => Blocks::TORCH());
+		$result->registerBlock("torchflower", fn() => Blocks::TORCHFLOWER());
 		$result->registerBlock("trapdoor", fn() => Blocks::OAK_TRAPDOOR());
 		$result->registerBlock("trapped_chest", fn() => Blocks::TRAPPED_CHEST());
 		$result->registerBlock("trip_wire", fn() => Blocks::TRIPWIRE());
@@ -1103,6 +1113,7 @@ private static function registerBlocks(self $result) : void{
 		$result->registerBlock("warped_hyphae", fn() => Blocks::WARPED_HYPHAE()->setStripped(false));
 		$result->registerBlock("warped_planks", fn() => Blocks::WARPED_PLANKS());
 		$result->registerBlock("warped_pressure_plate", fn() => Blocks::WARPED_PRESSURE_PLATE());
+		$result->registerBlock("warped_roots", fn() => Blocks::WARPED_ROOTS());
 		$result->registerBlock("warped_sign", fn() => Blocks::WARPED_SIGN());
 		$result->registerBlock("warped_slab", fn() => Blocks::WARPED_SLAB());
 		$result->registerBlock("warped_stairs", fn() => Blocks::WARPED_STAIRS());
@@ -1119,7 +1130,7 @@ private static function registerBlocks(self $result) : void{
 		$result->registerBlock("wheat_block", fn() => Blocks::WHEAT());
 		$result->registerBlock("white_tulip", fn() => Blocks::WHITE_TULIP());
 		$result->registerBlock("wither_rose", fn() => Blocks::WITHER_ROSE());
-		$result->registerBlock("wither_skeleton_skull", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::WITHER_SKELETON()));
+		$result->registerBlock("wither_skeleton_skull", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::WITHER_SKELETON));
 		$result->registerBlock("wood", fn() => Blocks::OAK_LOG()->setStripped(false));
 		$result->registerBlock("wood2", fn() => Blocks::ACACIA_LOG()->setStripped(false));
 		$result->registerBlock("wood_door_block", fn() => Blocks::OAK_DOOR());
@@ -1139,33 +1150,38 @@ private static function registerBlocks(self $result) : void{
 		$result->registerBlock("wool", fn() => Blocks::WOOL());
 		$result->registerBlock("workbench", fn() => Blocks::CRAFTING_TABLE());
 		$result->registerBlock("yellow_flower", fn() => Blocks::DANDELION());
-		$result->registerBlock("zombie_head", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::ZOMBIE()));
+		$result->registerBlock("zombie_head", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::ZOMBIE));
 	}
 
 	private static function registerDynamicItems(self $result) : void{
-		foreach(DyeColor::getAll() as $color){
-			$prefix = fn(string $name) => $color->name() . "_" . $name;
+		foreach(DyeColor::cases() as $color){
+			$prefix = fn(string $name) => strtolower($color->name) . "_" . $name;
 
 			$result->register($prefix("dye"), fn() => Items::DYE()->setColor($color));
 		}
-		foreach(SuspiciousStewType::getAll() as $suspiciousStewType){
-			$prefix = fn(string $name) => $suspiciousStewType->name() . "_" . $name;
+		foreach(SuspiciousStewType::cases() as $suspiciousStewType){
+			$prefix = fn(string $name) => strtolower($suspiciousStewType->name) . "_" . $name;
 
 			$result->register($prefix("suspicious_stew"), fn() => Items::SUSPICIOUS_STEW()->setType($suspiciousStewType));
 		}
+
+		foreach(PotionType::cases() as $potionType){
+			$prefix = fn(string $name) => strtolower($potionType->name) . "_" . $name;
+
+			$result->register($prefix("potion"), fn() => Items::POTION()->setType($potionType));
+			$result->register($prefix("splash_potion"), fn() => Items::SPLASH_POTION()->setType($potionType));
+		}
 	}
 
 	private static function registerItems(self $result) : void{
 
 		$result->register("acacia_boat", fn() => Items::ACACIA_BOAT());
 		$result->register("amethyst_shard", fn() => Items::AMETHYST_SHARD());
-		$result->register("antidote", fn() => Items::MEDICINE()->setType(MedicineType::ANTIDOTE()));
+		$result->register("antidote", fn() => Items::MEDICINE()->setType(MedicineType::ANTIDOTE));
 		$result->register("apple", fn() => Items::APPLE());
 		$result->register("apple_enchanted", fn() => Items::ENCHANTED_GOLDEN_APPLE());
 		$result->register("appleenchanted", fn() => Items::ENCHANTED_GOLDEN_APPLE());
 		$result->register("arrow", fn() => Items::ARROW());
-		$result->register("awkward_potion", fn() => Items::POTION()->setType(PotionType::AWKWARD()));
-		$result->register("awkward_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::AWKWARD()));
 		$result->register("baked_potato", fn() => Items::BAKED_POTATO());
 		$result->register("baked_potatoes", fn() => Items::BAKED_POTATO());
 		$result->register("beef", fn() => Items::RAW_BEEF());
@@ -1275,19 +1291,17 @@ private static function registerItems(self $result) : void{
 		$result->register("dye", fn() => Items::INK_SAC());
 		$result->register("echo_shard", fn() => Items::ECHO_SHARD());
 		$result->register("egg", fn() => Items::EGG());
-		$result->register("elixir", fn() => Items::MEDICINE()->setType(MedicineType::ELIXIR()));
+		$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());
 		$result->register("experience_bottle", fn() => Items::EXPERIENCE_BOTTLE());
-		$result->register("eye_drops", fn() => Items::MEDICINE()->setType(MedicineType::EYE_DROPS()));
+		$result->register("eye_drops", fn() => Items::MEDICINE()->setType(MedicineType::EYE_DROPS));
 		$result->register("feather", fn() => Items::FEATHER());
 		$result->register("fermented_spider_eye", fn() => Items::FERMENTED_SPIDER_EYE());
 		$result->register("fire_charge", fn() => Items::FIRE_CHARGE());
-		$result->register("fire_resistance_potion", fn() => Items::POTION()->setType(PotionType::FIRE_RESISTANCE()));
-		$result->register("fire_resistance_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::FIRE_RESISTANCE()));
 		$result->register("fish", fn() => Items::RAW_FISH());
 		$result->register("fishing_rod", fn() => Items::FISHING_ROD());
 		$result->register("flint", fn() => Items::FLINT());
@@ -1323,16 +1337,10 @@ private static function registerItems(self $result) : void{
 		$result->register("golden_shovel", fn() => Items::GOLDEN_SHOVEL());
 		$result->register("golden_sword", fn() => Items::GOLDEN_SWORD());
 		$result->register("gunpowder", fn() => Items::GUNPOWDER());
-		$result->register("harming_potion", fn() => Items::POTION()->setType(PotionType::HARMING()));
-		$result->register("harming_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::HARMING()));
-		$result->register("healing_potion", fn() => Items::POTION()->setType(PotionType::HEALING()));
-		$result->register("healing_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::HEALING()));
 		$result->register("heart_of_the_sea", fn() => Items::HEART_OF_THE_SEA());
 		$result->register("honey_bottle", fn() => Items::HONEY_BOTTLE());
 		$result->register("honeycomb", fn() => Items::HONEYCOMB());
 		$result->register("ink_sac", fn() => Items::INK_SAC());
-		$result->register("invisibility_potion", fn() => Items::POTION()->setType(PotionType::INVISIBILITY()));
-		$result->register("invisibility_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::INVISIBILITY()));
 		$result->register("iron_axe", fn() => Items::IRON_AXE());
 		$result->register("iron_boots", fn() => Items::IRON_BOOTS());
 		$result->register("iron_chestplate", fn() => Items::IRON_CHESTPLATE());
@@ -1347,8 +1355,6 @@ private static function registerItems(self $result) : void{
 		$result->register("jungle_boat", fn() => Items::JUNGLE_BOAT());
 		$result->register("lapis_lazuli", fn() => Items::LAPIS_LAZULI());
 		$result->register("lava_bucket", fn() => Items::LAVA_BUCKET());
-		$result->register("leaping_potion", fn() => Items::POTION()->setType(PotionType::LEAPING()));
-		$result->register("leaping_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LEAPING()));
 		$result->register("leather", fn() => Items::LEATHER());
 		$result->register("leather_boots", fn() => Items::LEATHER_BOOTS());
 		$result->register("leather_cap", fn() => Items::LEATHER_CAP());
@@ -1357,42 +1363,12 @@ private static function registerItems(self $result) : void{
 		$result->register("leather_leggings", fn() => Items::LEATHER_PANTS());
 		$result->register("leather_pants", fn() => Items::LEATHER_PANTS());
 		$result->register("leather_tunic", fn() => Items::LEATHER_TUNIC());
-		$result->register("long_fire_resistance_potion", fn() => Items::POTION()->setType(PotionType::LONG_FIRE_RESISTANCE()));
-		$result->register("long_fire_resistance_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_FIRE_RESISTANCE()));
-		$result->register("long_invisibility_potion", fn() => Items::POTION()->setType(PotionType::LONG_INVISIBILITY()));
-		$result->register("long_invisibility_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_INVISIBILITY()));
-		$result->register("long_leaping_potion", fn() => Items::POTION()->setType(PotionType::LONG_LEAPING()));
-		$result->register("long_leaping_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_LEAPING()));
-		$result->register("long_mundane_potion", fn() => Items::POTION()->setType(PotionType::LONG_MUNDANE()));
-		$result->register("long_mundane_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_MUNDANE()));
-		$result->register("long_night_vision_potion", fn() => Items::POTION()->setType(PotionType::LONG_NIGHT_VISION()));
-		$result->register("long_night_vision_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_NIGHT_VISION()));
-		$result->register("long_poison_potion", fn() => Items::POTION()->setType(PotionType::LONG_POISON()));
-		$result->register("long_poison_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_POISON()));
-		$result->register("long_regeneration_potion", fn() => Items::POTION()->setType(PotionType::LONG_REGENERATION()));
-		$result->register("long_regeneration_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_REGENERATION()));
-		$result->register("long_slow_falling_potion", fn() => Items::POTION()->setType(PotionType::LONG_SLOW_FALLING()));
-		$result->register("long_slow_falling_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_SLOW_FALLING()));
-		$result->register("long_slowness_potion", fn() => Items::POTION()->setType(PotionType::LONG_SLOWNESS()));
-		$result->register("long_slowness_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_SLOWNESS()));
-		$result->register("long_strength_potion", fn() => Items::POTION()->setType(PotionType::LONG_STRENGTH()));
-		$result->register("long_strength_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_STRENGTH()));
-		$result->register("long_swiftness_potion", fn() => Items::POTION()->setType(PotionType::LONG_SWIFTNESS()));
-		$result->register("long_swiftness_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_SWIFTNESS()));
-		$result->register("long_turtle_master_potion", fn() => Items::POTION()->setType(PotionType::LONG_TURTLE_MASTER()));
-		$result->register("long_turtle_master_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_TURTLE_MASTER()));
-		$result->register("long_water_breathing_potion", fn() => Items::POTION()->setType(PotionType::LONG_WATER_BREATHING()));
-		$result->register("long_water_breathing_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_WATER_BREATHING()));
-		$result->register("long_weakness_potion", fn() => Items::POTION()->setType(PotionType::LONG_WEAKNESS()));
-		$result->register("long_weakness_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_WEAKNESS()));
 		$result->register("magma_cream", fn() => Items::MAGMA_CREAM());
 		$result->register("melon", fn() => Items::MELON());
 		$result->register("melon_seeds", fn() => Items::MELON_SEEDS());
 		$result->register("melon_slice", fn() => Items::MELON());
 		$result->register("milk_bucket", fn() => Items::MILK_BUCKET());
 		$result->register("minecart", fn() => Items::MINECART());
-		$result->register("mundane_potion", fn() => Items::POTION()->setType(PotionType::MUNDANE()));
-		$result->register("mundane_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::MUNDANE()));
 		$result->register("mushroom_stew", fn() => Items::MUSHROOM_STEW());
 		$result->register("mutton", fn() => Items::RAW_MUTTON());
 		$result->register("mutton_cooked", fn() => Items::COOKED_MUTTON());
@@ -1416,14 +1392,10 @@ private static function registerItems(self $result) : void{
 		$result->register("netherite_shovel", fn() => Items::NETHERITE_SHOVEL());
 		$result->register("netherite_sword", fn() => Items::NETHERITE_SWORD());
 		$result->register("netherstar", fn() => Items::NETHER_STAR());
-		$result->register("night_vision_potion", fn() => Items::POTION()->setType(PotionType::NIGHT_VISION()));
-		$result->register("night_vision_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::NIGHT_VISION()));
 		$result->register("oak_boat", fn() => Items::OAK_BOAT());
 		$result->register("painting", fn() => Items::PAINTING());
 		$result->register("paper", fn() => Items::PAPER());
 		$result->register("phantom_membrane", fn() => Items::PHANTOM_MEMBRANE());
-		$result->register("poison_potion", fn() => Items::POTION()->setType(PotionType::POISON()));
-		$result->register("poison_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::POISON()));
 		$result->register("poisonous_potato", fn() => Items::POISONOUS_POTATO());
 		$result->register("popped_chorus_fruit", fn() => Items::POPPED_CHORUS_FRUIT());
 		$result->register("porkchop", fn() => Items::RAW_PORKCHOP());
@@ -1468,8 +1440,6 @@ private static function registerItems(self $result) : void{
 		$result->register("record_ward", fn() => Items::RECORD_WARD());
 		$result->register("redstone", fn() => Items::REDSTONE_DUST());
 		$result->register("redstone_dust", fn() => Items::REDSTONE_DUST());
-		$result->register("regeneration_potion", fn() => Items::POTION()->setType(PotionType::REGENERATION()));
-		$result->register("regeneration_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::REGENERATION()));
 		$result->register("rotten_flesh", fn() => Items::ROTTEN_FLESH());
 		$result->register("salmon", fn() => Items::RAW_SALMON());
 		$result->register("scute", fn() => Items::SCUTE());
@@ -1478,10 +1448,6 @@ private static function registerItems(self $result) : void{
 		$result->register("shulker_shell", fn() => Items::SHULKER_SHELL());
 		$result->register("slime_ball", fn() => Items::SLIMEBALL());
 		$result->register("slimeball", fn() => Items::SLIMEBALL());
-		$result->register("slow_falling_potion", fn() => Items::POTION()->setType(PotionType::SLOW_FALLING()));
-		$result->register("slow_falling_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::SLOW_FALLING()));
-		$result->register("slowness_potion", fn() => Items::POTION()->setType(PotionType::SLOWNESS()));
-		$result->register("slowness_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::SLOWNESS()));
 		$result->register("snowball", fn() => Items::SNOWBALL());
 		$result->register("speckled_melon", fn() => Items::GLISTERING_MELON());
 		$result->register("spider_eye", fn() => Items::SPIDER_EYE());
@@ -1497,52 +1463,19 @@ private static function registerItems(self $result) : void{
 		$result->register("stone_pickaxe", fn() => Items::STONE_PICKAXE());
 		$result->register("stone_shovel", fn() => Items::STONE_SHOVEL());
 		$result->register("stone_sword", fn() => Items::STONE_SWORD());
-		$result->register("strength_potion", fn() => Items::POTION()->setType(PotionType::STRENGTH()));
-		$result->register("strength_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRENGTH()));
 		$result->register("string", fn() => Items::STRING());
-		$result->register("strong_harming_potion", fn() => Items::POTION()->setType(PotionType::STRONG_HARMING()));
-		$result->register("strong_harming_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_HARMING()));
-		$result->register("strong_healing_potion", fn() => Items::POTION()->setType(PotionType::STRONG_HEALING()));
-		$result->register("strong_healing_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_HEALING()));
-		$result->register("strong_leaping_potion", fn() => Items::POTION()->setType(PotionType::STRONG_LEAPING()));
-		$result->register("strong_leaping_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_LEAPING()));
-		$result->register("strong_poison_potion", fn() => Items::POTION()->setType(PotionType::STRONG_POISON()));
-		$result->register("strong_poison_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_POISON()));
-		$result->register("strong_regeneration_potion", fn() => Items::POTION()->setType(PotionType::STRONG_REGENERATION()));
-		$result->register("strong_regeneration_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_REGENERATION()));
-		$result->register("strong_slowness_potion", fn() => Items::POTION()->setType(PotionType::STRONG_SLOWNESS()));
-		$result->register("strong_slowness_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_SLOWNESS()));
-		$result->register("strong_strength_potion", fn() => Items::POTION()->setType(PotionType::STRONG_STRENGTH()));
-		$result->register("strong_strength_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_STRENGTH()));
-		$result->register("strong_swiftness_potion", fn() => Items::POTION()->setType(PotionType::STRONG_SWIFTNESS()));
-		$result->register("strong_swiftness_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_SWIFTNESS()));
-		$result->register("strong_turtle_master_potion", fn() => Items::POTION()->setType(PotionType::STRONG_TURTLE_MASTER()));
-		$result->register("strong_turtle_master_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_TURTLE_MASTER()));
 		$result->register("sugar", fn() => Items::SUGAR());
 		$result->register("suspicious_stew", fn() => Items::SUSPICIOUS_STEW());
 		$result->register("sweet_berries", fn() => Items::SWEET_BERRIES());
-		$result->register("swiftness_potion", fn() => Items::POTION()->setType(PotionType::SWIFTNESS()));
-		$result->register("swiftness_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::SWIFTNESS()));
-		$result->register("thick_potion", fn() => Items::POTION()->setType(PotionType::THICK()));
-		$result->register("thick_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::THICK()));
-		$result->register("tonic", fn() => Items::MEDICINE()->setType(MedicineType::TONIC()));
+		$result->register("tonic", fn() => Items::MEDICINE()->setType(MedicineType::TONIC));
+		$result->register("torchflower_seeds", fn() => Items::TORCHFLOWER_SEEDS());
 		$result->register("totem", fn() => Items::TOTEM());
 		$result->register("turtle_helmet", fn() => Items::TURTLE_HELMET());
-		$result->register("turtle_master_potion", fn() => Items::POTION()->setType(PotionType::TURTLE_MASTER()));
-		$result->register("turtle_master_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::TURTLE_MASTER()));
 		$result->register("turtle_shell_piece", fn() => Items::SCUTE());
 		$result->register("villager_spawn_egg", fn() => Items::VILLAGER_SPAWN_EGG());
-		$result->register("water_breathing_potion", fn() => Items::POTION()->setType(PotionType::WATER_BREATHING()));
-		$result->register("water_breathing_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::WATER_BREATHING()));
 		$result->register("water_bucket", fn() => Items::WATER_BUCKET());
-		$result->register("water_potion", fn() => Items::POTION()->setType(PotionType::WATER()));
-		$result->register("water_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::WATER()));
-		$result->register("weakness_potion", fn() => Items::POTION()->setType(PotionType::WEAKNESS()));
-		$result->register("weakness_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::WEAKNESS()));
 		$result->register("wheat", fn() => Items::WHEAT());
 		$result->register("wheat_seeds", fn() => Items::WHEAT_SEEDS());
-		$result->register("wither_potion", fn() => Items::POTION()->setType(PotionType::WITHER()));
-		$result->register("wither_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::WITHER()));
 		$result->register("wooden_axe", fn() => Items::WOODEN_AXE());
 		$result->register("wooden_hoe", fn() => Items::WOODEN_HOE());
 		$result->register("wooden_pickaxe", fn() => Items::WOODEN_PICKAXE());
diff --git a/src/item/SuspiciousStew.php b/src/item/SuspiciousStew.php
index a2adc0b6176..7d1d30cc091 100644
--- a/src/item/SuspiciousStew.php
+++ b/src/item/SuspiciousStew.php
@@ -27,15 +27,10 @@
 
 class SuspiciousStew extends Food{
 
-	private SuspiciousStewType $suspiciousStewType;
-
-	public function __construct(ItemIdentifier $identifier, string $name){
-		$this->suspiciousStewType = SuspiciousStewType::POPPY();
-		parent::__construct($identifier, $name);
-	}
+	private SuspiciousStewType $suspiciousStewType = SuspiciousStewType::POPPY;
 
 	protected function describeState(RuntimeDataDescriber $w) : void{
-		$w->suspiciousStewType($this->suspiciousStewType);
+		$w->enum($this->suspiciousStewType);
 	}
 
 	public function getType() : SuspiciousStewType{ return $this->suspiciousStewType; }
diff --git a/src/item/SuspiciousStewType.php b/src/item/SuspiciousStewType.php
index 27209b5731e..d53b82a3fab 100644
--- a/src/item/SuspiciousStewType.php
+++ b/src/item/SuspiciousStewType.php
@@ -25,13 +25,11 @@
 
 use pocketmine\entity\effect\EffectInstance;
 use pocketmine\entity\effect\VanillaEffects;
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static SuspiciousStewType ALLIUM()
  * @method static SuspiciousStewType AZURE_BLUET()
@@ -44,61 +42,36 @@
  * @method static SuspiciousStewType TULIP()
  * @method static SuspiciousStewType WITHER_ROSE()
  */
-final class SuspiciousStewType{
-	use EnumTrait {
-		__construct as Enum___construct;
-	}
-
-	protected static function setup() : void{
-		self::registerAll(
-			new self("poppy", fn() => [
-				new EffectInstance(VanillaEffects::NIGHT_VISION(), 80)
-			]),
-			new self("cornflower", fn() => [
-				new EffectInstance(VanillaEffects::JUMP_BOOST(), 80)
-			]),
-			new self("tulip", fn() => [
-				new EffectInstance(VanillaEffects::WEAKNESS(), 140)
-			]),
-			new self("azure_bluet", fn() => [
-				new EffectInstance(VanillaEffects::BLINDNESS(), 120)
-			]),
-			new self("lily_of_the_valley", fn() => [
-				new EffectInstance(VanillaEffects::POISON(), 200)
-			]),
-			new self("dandelion", fn() => [
-				new EffectInstance(VanillaEffects::SATURATION(), 6)
-			]),
-			new self("blue_orchid", fn() => [
-				new EffectInstance(VanillaEffects::SATURATION(), 6)
-			]),
-			new self("allium", fn() => [
-				new EffectInstance(VanillaEffects::FIRE_RESISTANCE(), 40)
-			]),
-			new self("oxeye_daisy", fn() => [
-				new EffectInstance(VanillaEffects::REGENERATION(), 120)
-			]),
-			new self("wither_rose", fn() => [
-				new EffectInstance(VanillaEffects::WITHER(), 120)
-			])
-		);
-	}
+enum SuspiciousStewType{
+	use LegacyEnumShimTrait;
 
-	/**
-	 * @phpstan-param \Closure() : list<EffectInstance> $effectsGetter
-	 */
-	private function __construct(
-		string $enumName,
-		private \Closure $effectsGetter
-	){
-		$this->Enum___construct($enumName);
-	}
+	case POPPY;
+	case CORNFLOWER;
+	case TULIP;
+	case AZURE_BLUET;
+	case LILY_OF_THE_VALLEY;
+	case DANDELION;
+	case BLUE_ORCHID;
+	case ALLIUM;
+	case OXEYE_DAISY;
+	case WITHER_ROSE;
 
 	/**
 	 * @return EffectInstance[]
 	 * @phpstan-return list<EffectInstance>
 	 */
 	public function getEffects() : array{
-		return ($this->effectsGetter)();
+		return match($this){
+			self::POPPY => [new EffectInstance(VanillaEffects::NIGHT_VISION(), 80)],
+			self::CORNFLOWER => [new EffectInstance(VanillaEffects::JUMP_BOOST(), 80)],
+			self::TULIP => [new EffectInstance(VanillaEffects::WEAKNESS(), 140)],
+			self::AZURE_BLUET => [new EffectInstance(VanillaEffects::BLINDNESS(), 120)],
+			self::LILY_OF_THE_VALLEY => [new EffectInstance(VanillaEffects::POISON(), 200)],
+			self::DANDELION,
+			self::BLUE_ORCHID => [new EffectInstance(VanillaEffects::SATURATION(), 6)],
+			self::ALLIUM => [new EffectInstance(VanillaEffects::FIRE_RESISTANCE(), 40)],
+			self::OXEYE_DAISY => [new EffectInstance(VanillaEffects::REGENERATION(), 120)],
+			self::WITHER_ROSE => [new EffectInstance(VanillaEffects::WITHER(), 120)]
+		};
 	}
 }
diff --git a/src/item/TieredTool.php b/src/item/TieredTool.php
index dc00aebcf85..20b40bbcb40 100644
--- a/src/item/TieredTool.php
+++ b/src/item/TieredTool.php
@@ -51,7 +51,7 @@ public function getEnchantability() : int{
 	}
 
 	public function getFuelTime() : int{
-		if($this->tier->equals(ToolTier::WOOD())){
+		if($this->tier === ToolTier::WOOD){
 			return 200;
 		}
 
@@ -59,6 +59,6 @@ public function getFuelTime() : int{
 	}
 
 	public function isFireProof() : bool{
-		return $this->tier->equals(ToolTier::NETHERITE());
+		return $this->tier === ToolTier::NETHERITE;
 	}
 }
diff --git a/src/item/ToolTier.php b/src/item/ToolTier.php
index 4ca910c0bb0..8469bc7e5f4 100644
--- a/src/item/ToolTier.php
+++ b/src/item/ToolTier.php
@@ -23,13 +23,11 @@
 
 namespace pocketmine\item;
 
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static ToolTier DIAMOND()
  * @method static ToolTier GOLD()
@@ -37,48 +35,55 @@
  * @method static ToolTier NETHERITE()
  * @method static ToolTier STONE()
  * @method static ToolTier WOOD()
+ *
+ * @phpstan-type TMetadata array{0: int, 1: int, 2: int, 3: int, 4: int}
  */
-final class ToolTier{
-	use EnumTrait {
-		__construct as Enum___construct;
-	}
+enum ToolTier{
+	use LegacyEnumShimTrait;
 
-	protected static function setup() : void{
-		self::registerAll(
-			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)
-		);
+	case WOOD;
+	case GOLD;
+	case STONE;
+	case IRON;
+	case DIAMOND;
+	case NETHERITE;
+
+	/**
+	 * This function exists only to permit the use of named arguments and to make the code easier to read in PhpStorm.
+	 * @phpstan-return TMetadata
+	 */
+	private static function meta(int $harvestLevel, int $maxDurability, int $baseAttackPoints, int $baseEfficiency, int $enchantability) : array{
+		return [$harvestLevel, $maxDurability, $baseAttackPoints, $baseEfficiency, $enchantability];
 	}
 
-	private function __construct(
-		string $name,
-		private int $harvestLevel,
-		private int $maxDurability,
-		private int $baseAttackPoints,
-		private int $baseEfficiency,
-		private int $enchantability
-	){
-		$this->Enum___construct($name);
+	/**
+	 * @phpstan-return TMetadata
+	 */
+	private function getMetadata() : array{
+		return match($this){
+			self::WOOD => self::meta(1, 60, 5, 2, 15),
+			self::GOLD => self::meta(2, 33, 5, 12, 22),
+			self::STONE => self::meta(3, 132, 6, 4, 5),
+			self::IRON => self::meta(4, 251, 7, 6, 14),
+			self::DIAMOND => self::meta(5, 1562, 8, 8, 10),
+			self::NETHERITE => self::meta(6, 2032, 9, 9, 15)
+		};
 	}
 
 	public function getHarvestLevel() : int{
-		return $this->harvestLevel;
+		return $this->getMetadata()[0];
 	}
 
 	public function getMaxDurability() : int{
-		return $this->maxDurability;
+		return $this->getMetadata()[1];
 	}
 
 	public function getBaseAttackPoints() : int{
-		return $this->baseAttackPoints;
+		return $this->getMetadata()[2];
 	}
 
 	public function getBaseEfficiency() : int{
-		return $this->baseEfficiency;
+		return $this->getMetadata()[3];
 	}
 
 	/**
@@ -88,6 +93,6 @@ public function getBaseEfficiency() : int{
 	 * or multiple enchantments upon being enchanted in an enchanting table.
 	 */
 	public function getEnchantability() : int{
-		return $this->enchantability;
+		return $this->getMetadata()[4];
 	}
 }
diff --git a/src/item/TorchflowerSeeds.php b/src/item/TorchflowerSeeds.php
new file mode 100644
index 00000000000..123af35a090
--- /dev/null
+++ b/src/item/TorchflowerSeeds.php
@@ -0,0 +1,34 @@
+<?php
+
+/*
+ *
+ *  ____            _        _   __  __ _                  __  __ ____
+ * |  _ \ ___   ___| | _____| |_|  \/  (_)_ __   ___      |  \/  |  _ \
+ * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
+ * |  __/ (_) | (__|   <  __/ |_| |  | | | | | |  __/_____| |  | |  __/
+ * |_|   \___/ \___|_|\_\___|\__|_|  |_|_|_| |_|\___|     |_|  |_|_|
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * @author PocketMine Team
+ * @link http://www.pocketmine.net/
+ *
+ *
+ */
+
+declare(strict_types=1);
+
+namespace pocketmine\item;
+
+use pocketmine\block\Block;
+use pocketmine\block\VanillaBlocks;
+
+final class TorchflowerSeeds extends Item{
+
+	public function getBlock(?int $clickedFace = null) : Block{
+		return VanillaBlocks::TORCHFLOWER_CROP();
+	}
+}
diff --git a/src/item/VanillaItems.php b/src/item/VanillaItems.php
index a5d50c9db39..e07d4161b2f 100644
--- a/src/item/VanillaItems.php
+++ b/src/item/VanillaItems.php
@@ -36,9 +36,9 @@
 use pocketmine\item\ItemTypeIds as Ids;
 use pocketmine\item\VanillaArmorMaterials as ArmorMaterials;
 use pocketmine\math\Vector3;
-use pocketmine\utils\AssumptionFailedError;
 use pocketmine\utils\CloningRegistryTrait;
 use pocketmine\world\World;
+use function strtolower;
 
 /**
  * This doc-block is generated automatically, do not modify it manually.
@@ -296,6 +296,7 @@
  * @method static Item SUGAR()
  * @method static SuspiciousStew SUSPICIOUS_STEW()
  * @method static SweetBerries SWEET_BERRIES()
+ * @method static TorchflowerSeeds TORCHFLOWER_SEEDS()
  * @method static Totem TOTEM()
  * @method static TurtleHelmet TURTLE_HELMET()
  * @method static SpawnEgg VILLAGER_SPAWN_EGG()
@@ -506,21 +507,21 @@ public function isFireProof() : bool{ return true; }
 		self::register("raw_porkchop", new RawPorkchop(new IID(Ids::RAW_PORKCHOP), "Raw Porkchop"));
 		self::register("raw_rabbit", new RawRabbit(new IID(Ids::RAW_RABBIT), "Raw Rabbit"));
 		self::register("raw_salmon", new RawSalmon(new IID(Ids::RAW_SALMON), "Raw Salmon"));
-		self::register("record_11", new Record(new IID(Ids::RECORD_11), RecordType::DISK_11(), "Record 11"));
-		self::register("record_13", new Record(new IID(Ids::RECORD_13), RecordType::DISK_13(), "Record 13"));
-		self::register("record_5", new Record(new IID(Ids::RECORD_5), RecordType::DISK_5(), "Record 5"));
-		self::register("record_blocks", new Record(new IID(Ids::RECORD_BLOCKS), RecordType::DISK_BLOCKS(), "Record Blocks"));
-		self::register("record_cat", new Record(new IID(Ids::RECORD_CAT), RecordType::DISK_CAT(), "Record Cat"));
-		self::register("record_chirp", new Record(new IID(Ids::RECORD_CHIRP), RecordType::DISK_CHIRP(), "Record Chirp"));
-		self::register("record_far", new Record(new IID(Ids::RECORD_FAR), RecordType::DISK_FAR(), "Record Far"));
-		self::register("record_mall", new Record(new IID(Ids::RECORD_MALL), RecordType::DISK_MALL(), "Record Mall"));
-		self::register("record_mellohi", new Record(new IID(Ids::RECORD_MELLOHI), RecordType::DISK_MELLOHI(), "Record Mellohi"));
-		self::register("record_otherside", new Record(new IID(Ids::RECORD_OTHERSIDE), RecordType::DISK_OTHERSIDE(), "Record Otherside"));
-		self::register("record_pigstep", new Record(new IID(Ids::RECORD_PIGSTEP), RecordType::DISK_PIGSTEP(), "Record Pigstep"));
-		self::register("record_stal", new Record(new IID(Ids::RECORD_STAL), RecordType::DISK_STAL(), "Record Stal"));
-		self::register("record_strad", new Record(new IID(Ids::RECORD_STRAD), RecordType::DISK_STRAD(), "Record Strad"));
-		self::register("record_wait", new Record(new IID(Ids::RECORD_WAIT), RecordType::DISK_WAIT(), "Record Wait"));
-		self::register("record_ward", new Record(new IID(Ids::RECORD_WARD), RecordType::DISK_WARD(), "Record Ward"));
+		self::register("record_11", new Record(new IID(Ids::RECORD_11), RecordType::DISK_11, "Record 11"));
+		self::register("record_13", new Record(new IID(Ids::RECORD_13), RecordType::DISK_13, "Record 13"));
+		self::register("record_5", new Record(new IID(Ids::RECORD_5), RecordType::DISK_5, "Record 5"));
+		self::register("record_blocks", new Record(new IID(Ids::RECORD_BLOCKS), RecordType::DISK_BLOCKS, "Record Blocks"));
+		self::register("record_cat", new Record(new IID(Ids::RECORD_CAT), RecordType::DISK_CAT, "Record Cat"));
+		self::register("record_chirp", new Record(new IID(Ids::RECORD_CHIRP), RecordType::DISK_CHIRP, "Record Chirp"));
+		self::register("record_far", new Record(new IID(Ids::RECORD_FAR), RecordType::DISK_FAR, "Record Far"));
+		self::register("record_mall", new Record(new IID(Ids::RECORD_MALL), RecordType::DISK_MALL, "Record Mall"));
+		self::register("record_mellohi", new Record(new IID(Ids::RECORD_MELLOHI), RecordType::DISK_MELLOHI, "Record Mellohi"));
+		self::register("record_otherside", new Record(new IID(Ids::RECORD_OTHERSIDE), RecordType::DISK_OTHERSIDE, "Record Otherside"));
+		self::register("record_pigstep", new Record(new IID(Ids::RECORD_PIGSTEP), RecordType::DISK_PIGSTEP, "Record Pigstep"));
+		self::register("record_stal", new Record(new IID(Ids::RECORD_STAL), RecordType::DISK_STAL, "Record Stal"));
+		self::register("record_strad", new Record(new IID(Ids::RECORD_STRAD), RecordType::DISK_STRAD, "Record Strad"));
+		self::register("record_wait", new Record(new IID(Ids::RECORD_WAIT), RecordType::DISK_WAIT, "Record Wait"));
+		self::register("record_ward", new Record(new IID(Ids::RECORD_WARD), RecordType::DISK_WARD, "Record Ward"));
 		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"));
@@ -538,6 +539,7 @@ public function isFireProof() : bool{ return true; }
 		self::register("sugar", new Item(new IID(Ids::SUGAR), "Sugar"));
 		self::register("suspicious_stew", new SuspiciousStew(new IID(Ids::SUSPICIOUS_STEW), "Suspicious Stew"));
 		self::register("sweet_berries", new SweetBerries(new IID(Ids::SWEET_BERRIES), "Sweet Berries"));
+		self::register("torchflower_seeds", new TorchflowerSeeds(new IID(Ids::TORCHFLOWER_SEEDS), "Torchflower Seeds"));
 		self::register("totem", new Totem(new IID(Ids::TOTEM), "Totem of Undying"));
 		self::register("warped_sign", new ItemBlockWallOrFloor(new IID(Ids::WARPED_SIGN), Blocks::WARPED_SIGN(), Blocks::WARPED_WALL_SIGN()));
 		self::register("water_bucket", new LiquidBucket(new IID(Ids::WATER_BUCKET), "Water Bucket", Blocks::WATER()));
@@ -546,17 +548,16 @@ public function isFireProof() : bool{ return true; }
 		self::register("writable_book", new WritableBook(new IID(Ids::WRITABLE_BOOK), "Book & Quill"));
 		self::register("written_book", new WrittenBook(new IID(Ids::WRITTEN_BOOK), "Written Book"));
 
-		foreach(BoatType::getAll() as $type){
+		foreach(BoatType::cases() as $type){
 			//boat type is static, because different types of wood may have different properties
-			self::register($type->name() . "_boat", new Boat(new IID(match($type){
-				BoatType::OAK() => Ids::OAK_BOAT,
-				BoatType::SPRUCE() => Ids::SPRUCE_BOAT,
-				BoatType::BIRCH() => Ids::BIRCH_BOAT,
-				BoatType::JUNGLE() => Ids::JUNGLE_BOAT,
-				BoatType::ACACIA() => Ids::ACACIA_BOAT,
-				BoatType::DARK_OAK() => Ids::DARK_OAK_BOAT,
-				BoatType::MANGROVE() => Ids::MANGROVE_BOAT,
-				default => throw new AssumptionFailedError("Unhandled tree type " . $type->name())
+			self::register(strtolower($type->name) . "_boat", new Boat(new IID(match($type){
+				BoatType::OAK => Ids::OAK_BOAT,
+				BoatType::SPRUCE => Ids::SPRUCE_BOAT,
+				BoatType::BIRCH => Ids::BIRCH_BOAT,
+				BoatType::JUNGLE => Ids::JUNGLE_BOAT,
+				BoatType::ACACIA => Ids::ACACIA_BOAT,
+				BoatType::DARK_OAK => Ids::DARK_OAK_BOAT,
+				BoatType::MANGROVE => Ids::MANGROVE_BOAT,
 			}), $type->getDisplayName() . " Boat", $type));
 		}
 	}
@@ -580,36 +581,36 @@ public function createEntity(World $world, Vector3 $pos, float $yaw, float $pitc
 	}
 
 	private static function registerTierToolItems() : void{
-		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]));
+		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{
diff --git a/src/network/mcpe/InventoryManager.php b/src/network/mcpe/InventoryManager.php
index ebbd7114689..938b5c82c29 100644
--- a/src/network/mcpe/InventoryManager.php
+++ b/src/network/mcpe/InventoryManager.php
@@ -352,11 +352,10 @@ protected static function createContainerOpen(int $id, Inventory $inv) : ?array{
 			$blockPosition = BlockPosition::fromVector3($inv->getHolder());
 			$windowType = match(true){
 				$inv instanceof LoomInventory => WindowTypes::LOOM,
-				$inv instanceof FurnaceInventory => match($inv->getFurnaceType()->id()){
-						FurnaceType::FURNACE()->id() => WindowTypes::FURNACE,
-						FurnaceType::BLAST_FURNACE()->id() => WindowTypes::BLAST_FURNACE,
-						FurnaceType::SMOKER()->id() => WindowTypes::SMOKER,
-						default => throw new AssumptionFailedError("Unreachable")
+				$inv instanceof FurnaceInventory => match($inv->getFurnaceType()){
+						FurnaceType::FURNACE => WindowTypes::FURNACE,
+						FurnaceType::BLAST_FURNACE => WindowTypes::BLAST_FURNACE,
+						FurnaceType::SMOKER => WindowTypes::SMOKER,
 					},
 				$inv instanceof EnchantInventory => WindowTypes::ENCHANTMENT,
 				$inv instanceof BrewingStandInventory => WindowTypes::BREWING_STAND,
diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php
index 5b65ca1b5f6..d0a4c000a3e 100644
--- a/src/network/mcpe/NetworkSession.php
+++ b/src/network/mcpe/NetworkSession.php
@@ -1069,7 +1069,7 @@ function(CompressBatchPromise $promise) use ($world, $onCompletion, $chunkX, $ch
 					$this->logger->debug("Tried to send no-longer-active chunk $chunkX $chunkZ in world " . $world->getFolderName());
 					return;
 				}
-				if(!$status->equals(UsedChunkStatus::REQUESTED_SENDING())){
+				if($status !== UsedChunkStatus::REQUESTED_SENDING){
 					//TODO: make this an error
 					//this could be triggered due to the shitty way that chunk resends are handled
 					//right now - not because of the spammy re-requesting, but because the chunk status reverts
diff --git a/src/network/mcpe/cache/CraftingDataCache.php b/src/network/mcpe/cache/CraftingDataCache.php
index aa70c7d35c8..1d4023b41e2 100644
--- a/src/network/mcpe/cache/CraftingDataCache.php
+++ b/src/network/mcpe/cache/CraftingDataCache.php
@@ -81,12 +81,11 @@ private function buildCraftingDataCache(CraftingManager $manager) : CraftingData
 
 		foreach($manager->getCraftingRecipeIndex() as $index => $recipe){
 			if($recipe instanceof ShapelessRecipe){
-				$typeTag = match($recipe->getType()->id()){
-					ShapelessRecipeType::CRAFTING()->id() => CraftingRecipeBlockName::CRAFTING_TABLE,
-					ShapelessRecipeType::STONECUTTER()->id() => CraftingRecipeBlockName::STONECUTTER,
-					ShapelessRecipeType::CARTOGRAPHY()->id() => CraftingRecipeBlockName::CARTOGRAPHY_TABLE,
-					ShapelessRecipeType::SMITHING()->id() => CraftingRecipeBlockName::SMITHING_TABLE,
-					default => throw new AssumptionFailedError("Unreachable"),
+				$typeTag = match($recipe->getType()){
+					ShapelessRecipeType::CRAFTING => CraftingRecipeBlockName::CRAFTING_TABLE,
+					ShapelessRecipeType::STONECUTTER => CraftingRecipeBlockName::STONECUTTER,
+					ShapelessRecipeType::CARTOGRAPHY => CraftingRecipeBlockName::CARTOGRAPHY_TABLE,
+					ShapelessRecipeType::SMITHING => CraftingRecipeBlockName::SMITHING_TABLE,
 				};
 				$recipesWithTypeIds[] = new ProtocolShapelessRecipe(
 					CraftingDataPacket::ENTRY_SHAPELESS,
@@ -121,12 +120,11 @@ private function buildCraftingDataCache(CraftingManager $manager) : CraftingData
 			}
 		}
 
-		foreach(FurnaceType::getAll() as $furnaceType){
-			$typeTag = match($furnaceType->id()){
-				FurnaceType::FURNACE()->id() => FurnaceRecipeBlockName::FURNACE,
-				FurnaceType::BLAST_FURNACE()->id() => FurnaceRecipeBlockName::BLAST_FURNACE,
-				FurnaceType::SMOKER()->id() => FurnaceRecipeBlockName::SMOKER,
-				default => throw new AssumptionFailedError("Unreachable"),
+		foreach(FurnaceType::cases() as $furnaceType){
+			$typeTag = match($furnaceType){
+				FurnaceType::FURNACE => FurnaceRecipeBlockName::FURNACE,
+				FurnaceType::BLAST_FURNACE => FurnaceRecipeBlockName::BLAST_FURNACE,
+				FurnaceType::SMOKER => FurnaceRecipeBlockName::SMOKER,
 			};
 			foreach($manager->getFurnaceRecipeManager($furnaceType)->getAll() as $recipe){
 				$input = $converter->coreRecipeIngredientToNet($recipe->getInput())->getDescriptor();
diff --git a/src/network/mcpe/convert/TypeConverter.php b/src/network/mcpe/convert/TypeConverter.php
index 94f476e9caf..53ce6e98a88 100644
--- a/src/network/mcpe/convert/TypeConverter.php
+++ b/src/network/mcpe/convert/TypeConverter.php
@@ -108,33 +108,23 @@ public function setSkinAdapter(SkinAdapter $skinAdapter) : void{
 	 * @internal
 	 */
 	public function coreGameModeToProtocol(GameMode $gamemode) : int{
-		switch($gamemode->id()){
-			case GameMode::SURVIVAL()->id():
-				return ProtocolGameMode::SURVIVAL;
-			case GameMode::CREATIVE()->id():
-			case GameMode::SPECTATOR()->id():
-				return ProtocolGameMode::CREATIVE;
-			case GameMode::ADVENTURE()->id():
-				return ProtocolGameMode::ADVENTURE;
-			default:
-				throw new AssumptionFailedError("Unknown game mode");
-		}
+		return match($gamemode){
+			GameMode::SURVIVAL => ProtocolGameMode::SURVIVAL,
+			//TODO: native spectator support
+			GameMode::CREATIVE, GameMode::SPECTATOR => ProtocolGameMode::CREATIVE,
+			GameMode::ADVENTURE => ProtocolGameMode::ADVENTURE,
+		};
 	}
 
 	public function protocolGameModeToCore(int $gameMode) : ?GameMode{
-		switch($gameMode){
-			case ProtocolGameMode::SURVIVAL:
-				return GameMode::SURVIVAL();
-			case ProtocolGameMode::CREATIVE:
-				return GameMode::CREATIVE();
-			case ProtocolGameMode::ADVENTURE:
-				return GameMode::ADVENTURE();
-			case ProtocolGameMode::CREATIVE_VIEWER:
-			case ProtocolGameMode::SURVIVAL_VIEWER:
-				return GameMode::SPECTATOR();
-			default:
-				return null;
-		}
+		return match($gameMode){
+			ProtocolGameMode::SURVIVAL => GameMode::SURVIVAL,
+			ProtocolGameMode::CREATIVE => GameMode::CREATIVE,
+			ProtocolGameMode::ADVENTURE => GameMode::ADVENTURE,
+			ProtocolGameMode::SURVIVAL_VIEWER, ProtocolGameMode::CREATIVE_VIEWER => GameMode::SPECTATOR,
+			//TODO: native spectator support
+			default => null,
+		};
 	}
 
 	public function coreRecipeIngredientToNet(?RecipeIngredient $ingredient) : ProtocolRecipeIngredient{
diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php
index 9a477d92960..32e788c6a36 100644
--- a/src/network/mcpe/handler/InGamePacketHandler.php
+++ b/src/network/mcpe/handler/InGamePacketHandler.php
@@ -783,7 +783,7 @@ public function handlePlayerInput(PlayerInputPacket $packet) : bool{
 
 	public function handleSetPlayerGameType(SetPlayerGameTypePacket $packet) : bool{
 		$gameMode = $this->session->getTypeConverter()->protocolGameModeToCore($packet->gamemode);
-		if($gameMode === null || !$gameMode->equals($this->player->getGamemode())){
+		if($gameMode !== $this->player->getGamemode()){
 			//Set this back to default. TODO: handle this properly
 			$this->session->syncGameMode($this->player->getGamemode(), true);
 		}
diff --git a/src/network/mcpe/handler/LoginPacketHandler.php b/src/network/mcpe/handler/LoginPacketHandler.php
index a8c3d4d628b..9ac82bc5fef 100644
--- a/src/network/mcpe/handler/LoginPacketHandler.php
+++ b/src/network/mcpe/handler/LoginPacketHandler.php
@@ -83,6 +83,9 @@ public function handleLogin(LoginPacket $packet) : bool{
 			throw new PacketHandlingException("Invalid login UUID");
 		}
 		$uuid = Uuid::fromString($extraData->identity);
+		$arrClientData = (array) $clientData;
+		$arrClientData["TitleID"] = $extraData->titleId;
+
 		if($extraData->XUID !== ""){
 			$playerInfo = new XboxLivePlayerInfo(
 				$extraData->XUID,
@@ -90,7 +93,7 @@ public function handleLogin(LoginPacket $packet) : bool{
 				$uuid,
 				$skin,
 				$clientData->LanguageCode,
-				(array) $clientData
+				$arrClientData
 			);
 		}else{
 			$playerInfo = new PlayerInfo(
@@ -98,7 +101,7 @@ public function handleLogin(LoginPacket $packet) : bool{
 				$uuid,
 				$skin,
 				$clientData->LanguageCode,
-				(array) $clientData
+				$arrClientData
 			);
 		}
 		($this->playerInfoConsumer)($playerInfo);
diff --git a/src/network/mcpe/raklib/RakLibInterface.php b/src/network/mcpe/raklib/RakLibInterface.php
index 759a992e80c..d189cf0fd3a 100644
--- a/src/network/mcpe/raklib/RakLibInterface.php
+++ b/src/network/mcpe/raklib/RakLibInterface.php
@@ -271,8 +271,8 @@ public function setName(string $name) : void{
 				$this->rakServerId,
 				$this->server->getName(),
 				match($this->server->getGamemode()){
-					GameMode::SURVIVAL() => "Survival",
-					GameMode::ADVENTURE() => "Adventure",
+					GameMode::SURVIVAL => "Survival",
+					GameMode::ADVENTURE => "Adventure",
 					default => "Creative"
 				}
 			]) . ";"
diff --git a/src/network/query/QueryInfo.php b/src/network/query/QueryInfo.php
index cdb1c66dbec..0bf5b4f65ee 100644
--- a/src/network/query/QueryInfo.php
+++ b/src/network/query/QueryInfo.php
@@ -71,7 +71,7 @@ public function __construct(Server $server){
 		$this->plugins = $server->getPluginManager()->getPlugins();
 		$this->players = array_map(fn(Player $p) => $p->getName(), $server->getOnlinePlayers());
 
-		$this->gametype = ($server->getGamemode()->equals(GameMode::SURVIVAL()) || $server->getGamemode()->equals(GameMode::ADVENTURE())) ? "SMP" : "CMP";
+		$this->gametype = ($server->getGamemode() === GameMode::SURVIVAL || $server->getGamemode() === GameMode::ADVENTURE) ? "SMP" : "CMP";
 		$this->version = $server->getVersion();
 		$this->server_engine = $server->getName() . " " . $server->getPocketMineVersion();
 		$world = $server->getWorldManager()->getDefaultWorld();
diff --git a/src/player/GameMode.php b/src/player/GameMode.php
index 0fc60ece462..519cbbe8ae4 100644
--- a/src/player/GameMode.php
+++ b/src/player/GameMode.php
@@ -25,73 +25,76 @@
 
 use pocketmine\lang\KnownTranslationFactory;
 use pocketmine\lang\Translatable;
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 use function mb_strtolower;
+use function spl_object_id;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static GameMode ADVENTURE()
  * @method static GameMode CREATIVE()
  * @method static GameMode SPECTATOR()
  * @method static GameMode SURVIVAL()
+ *
+ * @phpstan-type TMetadata array{0: string, 1: Translatable, 2: list<string>}
  */
-final class GameMode{
-	use EnumTrait {
-		__construct as Enum___construct;
-		register as Enum_register;
-	}
+enum GameMode{
+	use LegacyEnumShimTrait;
 
-	/** @var self[] */
-	protected static array $aliasMap = [];
+	case SURVIVAL;
+	case CREATIVE;
+	case ADVENTURE;
+	case SPECTATOR;
 
-	protected static function setup() : void{
-		self::registerAll(
-			new self("survival", "Survival", KnownTranslationFactory::gameMode_survival(), ["survival", "s", "0"]),
-			new self("creative", "Creative", KnownTranslationFactory::gameMode_creative(), ["creative", "c", "1"]),
-			new self("adventure", "Adventure", KnownTranslationFactory::gameMode_adventure(), ["adventure", "a", "2"]),
-			new self("spectator", "Spectator", KnownTranslationFactory::gameMode_spectator(), ["spectator", "v", "view", "3"])
-		);
-	}
+	public static function fromString(string $str) : ?self{
+		/**
+		 * @var self[]|null $aliasMap
+		 * @phpstan-var array<string, self>|null $aliasMap
+		 */
+		static $aliasMap = null;
 
-	protected static function register(self $member) : void{
-		self::Enum_register($member);
-		foreach($member->getAliases() as $alias){
-			self::$aliasMap[mb_strtolower($alias)] = $member;
+		if($aliasMap === null){
+			$aliasMap = [];
+			foreach(self::cases() as $case){
+				foreach($case->getAliases() as $alias){
+					$aliasMap[$alias] = $case;
+				}
+			}
 		}
-	}
 
-	public static function fromString(string $str) : ?self{
-		self::checkInit();
-		return self::$aliasMap[mb_strtolower($str)] ?? null;
+		return $aliasMap[mb_strtolower($str)] ?? null;
 	}
 
 	/**
-	 * @param string[] $aliases
+	 * @phpstan-return TMetadata
 	 */
-	private function __construct(
-		string $enumName,
-		private string $englishName,
-		private Translatable $translatableName,
-		private array $aliases = []
-	){
-		$this->Enum___construct($enumName);
+	private function getMetadata() : array{
+		/** @phpstan-var array<int, TMetadata> $cache */
+		static $cache = [];
+
+		return $cache[spl_object_id($this)] ??= match($this){
+			self::SURVIVAL => ["Survival", KnownTranslationFactory::gameMode_survival(), ["survival", "s", "0"]],
+			self::CREATIVE => ["Creative", KnownTranslationFactory::gameMode_creative(), ["creative", "c", "1"]],
+			self::ADVENTURE => ["Adventure", KnownTranslationFactory::gameMode_adventure(), ["adventure", "a", "2"]],
+			self::SPECTATOR => ["Spectator", KnownTranslationFactory::gameMode_spectator(), ["spectator", "v", "view", "3"]]
+		};
 	}
 
 	public function getEnglishName() : string{
-		return $this->englishName;
+		return $this->getMetadata()[0];
 	}
 
-	public function getTranslatableName() : Translatable{ return $this->translatableName; }
+	public function getTranslatableName() : Translatable{
+		return $this->getMetadata()[1];
+	}
 
 	/**
 	 * @return string[]
 	 */
 	public function getAliases() : array{
-		return $this->aliases;
+		return $this->getMetadata()[2];
 	}
 
 	//TODO: ability sets per gamemode
diff --git a/src/player/Player.php b/src/player/Player.php
index 2059613ad0c..0afa0bffe36 100644
--- a/src/player/Player.php
+++ b/src/player/Player.php
@@ -331,7 +331,7 @@ public function __construct(Server $server, NetworkSession $session, PlayerInfo
 		$zSpawnChunk = $spawnLocation->getFloorZ() >> Chunk::COORD_BIT_SIZE;
 		$world->registerChunkLoader($this->chunkLoader, $xSpawnChunk, $zSpawnChunk, true);
 		$world->registerChunkListener($this, $xSpawnChunk, $zSpawnChunk);
-		$this->usedChunks[World::chunkHash($xSpawnChunk, $zSpawnChunk)] = UsedChunkStatus::NEEDED();
+		$this->usedChunks[World::chunkHash($xSpawnChunk, $zSpawnChunk)] = UsedChunkStatus::NEEDED;
 
 		parent::__construct($spawnLocation, $this->playerInfo->getSkin(), $namedtag);
 	}
@@ -372,7 +372,7 @@ function() : void{
 		$this->lastPlayed = $nbt->getLong(self::TAG_LAST_PLAYED, $now);
 
 		if(!$this->server->getForceGamemode() && ($gameModeTag = $nbt->getTag(self::TAG_GAME_MODE)) instanceof IntTag){
-			$this->internalSetGameMode(GameModeIdMap::getInstance()->fromId($gameModeTag->getValue()) ?? GameMode::SURVIVAL()); //TODO: bad hack here to avoid crashes on corrupted data
+			$this->internalSetGameMode(GameModeIdMap::getInstance()->fromId($gameModeTag->getValue()) ?? GameMode::SURVIVAL); //TODO: bad hack here to avoid crashes on corrupted data
 		}else{
 			$this->internalSetGameMode($this->server->getGamemode());
 		}
@@ -768,7 +768,7 @@ protected function unloadChunk(int $x, int $z, ?World $world = null) : void{
 
 	protected function spawnEntitiesOnAllChunks() : void{
 		foreach($this->usedChunks as $chunkHash => $status){
-			if($status->equals(UsedChunkStatus::SENT())){
+			if($status === UsedChunkStatus::SENT){
 				World::getXZ($chunkHash, $chunkX, $chunkZ);
 				$this->spawnEntitiesOnChunk($chunkX, $chunkZ);
 			}
@@ -810,7 +810,7 @@ protected function requestChunks() : void{
 
 			++$count;
 
-			$this->usedChunks[$index] = UsedChunkStatus::REQUESTED_GENERATION();
+			$this->usedChunks[$index] = UsedChunkStatus::REQUESTED_GENERATION;
 			$this->activeChunkGenerationRequests[$index] = true;
 			unset($this->loadQueue[$index]);
 			$this->getWorld()->registerChunkLoader($this->chunkLoader, $X, $Z, true);
@@ -824,17 +824,17 @@ function() use ($X, $Z, $index, $world) : void{
 					if(!$this->isConnected() || !isset($this->usedChunks[$index]) || $world !== $this->getWorld()){
 						return;
 					}
-					if(!$this->usedChunks[$index]->equals(UsedChunkStatus::REQUESTED_GENERATION())){
+					if($this->usedChunks[$index] !== UsedChunkStatus::REQUESTED_GENERATION){
 						//We may have previously requested this, decided we didn't want it, and then decided we did want
 						//it again, all before the generation request got executed. In that case, the promise would have
 						//multiple callbacks for this player. In that case, only the first one matters.
 						return;
 					}
 					unset($this->activeChunkGenerationRequests[$index]);
-					$this->usedChunks[$index] = UsedChunkStatus::REQUESTED_SENDING();
+					$this->usedChunks[$index] = UsedChunkStatus::REQUESTED_SENDING;
 
 					$this->getNetworkSession()->startUsingChunk($X, $Z, function() use ($X, $Z, $index) : void{
-						$this->usedChunks[$index] = UsedChunkStatus::SENT();
+						$this->usedChunks[$index] = UsedChunkStatus::SENT;
 						if($this->spawnChunkLoadCount === -1){
 							$this->spawnEntitiesOnChunk($X, $Z);
 						}elseif($this->spawnChunkLoadCount++ === $this->spawnThreshold){
@@ -951,7 +951,7 @@ protected function orderChunks() : void{
 			$this->location->getFloorX() >> Chunk::COORD_BIT_SIZE,
 			$this->location->getFloorZ() >> Chunk::COORD_BIT_SIZE
 		) as $radius => $hash){
-			if(!isset($this->usedChunks[$hash]) || $this->usedChunks[$hash]->equals(UsedChunkStatus::NEEDED())){
+			if(!isset($this->usedChunks[$hash]) || $this->usedChunks[$hash] === UsedChunkStatus::NEEDED){
 				$newOrder[$hash] = true;
 			}
 			if($radius < $tickingChunkRadius){
@@ -1005,7 +1005,7 @@ public function getUsedChunkStatus(int $chunkX, int $chunkZ) : ?UsedChunkStatus{
 	 */
 	public function hasReceivedChunk(int $chunkX, int $chunkZ) : bool{
 		$status = $this->usedChunks[World::chunkHash($chunkX, $chunkZ)] ?? null;
-		return $status !== null && $status->equals(UsedChunkStatus::SENT());
+		return $status === UsedChunkStatus::SENT;
 	}
 
 	/**
@@ -1113,7 +1113,7 @@ public function getGamemode() : GameMode{
 	protected function internalSetGameMode(GameMode $gameMode) : void{
 		$this->gamemode = $gameMode;
 
-		$this->allowFlight = $this->gamemode->equals(GameMode::CREATIVE());
+		$this->allowFlight = $this->gamemode === GameMode::CREATIVE;
 		$this->hungerManager->setEnabled($this->isSurvival());
 
 		if($this->isSpectator()){
@@ -1139,7 +1139,7 @@ protected function internalSetGameMode(GameMode $gameMode) : void{
 	 * Sets the provided gamemode.
 	 */
 	public function setGamemode(GameMode $gm) : bool{
-		if($this->gamemode->equals($gm)){
+		if($this->gamemode === $gm){
 			return false;
 		}
 
@@ -1168,7 +1168,7 @@ public function setGamemode(GameMode $gm) : bool{
 	 * @param bool $literal whether a literal check should be performed
 	 */
 	public function isSurvival(bool $literal = false) : bool{
-		return $this->gamemode->equals(GameMode::SURVIVAL()) || (!$literal && $this->gamemode->equals(GameMode::ADVENTURE()));
+		return $this->gamemode === GameMode::SURVIVAL || (!$literal && $this->gamemode === GameMode::ADVENTURE);
 	}
 
 	/**
@@ -1178,7 +1178,7 @@ public function isSurvival(bool $literal = false) : bool{
 	 * @param bool $literal whether a literal check should be performed
 	 */
 	public function isCreative(bool $literal = false) : bool{
-		return $this->gamemode->equals(GameMode::CREATIVE()) || (!$literal && $this->gamemode->equals(GameMode::SPECTATOR()));
+		return $this->gamemode === GameMode::CREATIVE || (!$literal && $this->gamemode === GameMode::SPECTATOR);
 	}
 
 	/**
@@ -1188,18 +1188,18 @@ public function isCreative(bool $literal = false) : bool{
 	 * @param bool $literal whether a literal check should be performed
 	 */
 	public function isAdventure(bool $literal = false) : bool{
-		return $this->gamemode->equals(GameMode::ADVENTURE()) || (!$literal && $this->gamemode->equals(GameMode::SPECTATOR()));
+		return $this->gamemode === GameMode::ADVENTURE || (!$literal && $this->gamemode === GameMode::SPECTATOR);
 	}
 
 	public function isSpectator() : bool{
-		return $this->gamemode->equals(GameMode::SPECTATOR());
+		return $this->gamemode === GameMode::SPECTATOR;
 	}
 
 	/**
 	 * TODO: make this a dynamic ability instead of being hardcoded
 	 */
 	public function hasFiniteResources() : bool{
-		return !$this->gamemode->equals(GameMode::CREATIVE());
+		return $this->gamemode !== GameMode::CREATIVE;
 	}
 
 	public function getDrops() : array{
@@ -1612,7 +1612,7 @@ public function useHeldItem() : bool{
 
 		$returnedItems = [];
 		$result = $item->onClickAir($this, $directionVector, $returnedItems);
-		if($result->equals(ItemUseResult::FAIL())){
+		if($result === ItemUseResult::FAIL){
 			return false;
 		}
 
@@ -1672,7 +1672,7 @@ public function releaseHeldItem() : bool{
 
 			$returnedItems = [];
 			$result = $item->onReleaseUsing($this, $returnedItems);
-			if($result->equals(ItemUseResult::SUCCESS())){
+			if($result === ItemUseResult::SUCCESS){
 				$this->resetItemCooldown($item);
 				$this->returnItemsFromAction($oldItem, $item, $returnedItems);
 				return true;
@@ -2694,8 +2694,8 @@ public function openSignEditor(Vector3 $position) : void{
 
 	public function onChunkChanged(int $chunkX, int $chunkZ, Chunk $chunk) : void{
 		$status = $this->usedChunks[$hash = World::chunkHash($chunkX, $chunkZ)] ?? null;
-		if($status !== null && $status->equals(UsedChunkStatus::SENT())){
-			$this->usedChunks[$hash] = UsedChunkStatus::NEEDED();
+		if($status === UsedChunkStatus::SENT){
+			$this->usedChunks[$hash] = UsedChunkStatus::NEEDED;
 			$this->nextChunkOrderRun = 0;
 		}
 	}
diff --git a/src/player/UsedChunkStatus.php b/src/player/UsedChunkStatus.php
index 69e249a24dc..dda41fe7e70 100644
--- a/src/player/UsedChunkStatus.php
+++ b/src/player/UsedChunkStatus.php
@@ -23,28 +23,22 @@
 
 namespace pocketmine\player;
 
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static UsedChunkStatus NEEDED()
  * @method static UsedChunkStatus REQUESTED_GENERATION()
  * @method static UsedChunkStatus REQUESTED_SENDING()
  * @method static UsedChunkStatus SENT()
  */
-final class UsedChunkStatus{
-	use EnumTrait;
+enum UsedChunkStatus{
+	use LegacyEnumShimTrait;
 
-	protected static function setup() : void{
-		self::registerAll(
-			new self("NEEDED"),
-			new self("REQUESTED_GENERATION"),
-			new self("REQUESTED_SENDING"),
-			new self("SENT")
-		);
-	}
+	case NEEDED;
+	case REQUESTED_GENERATION;
+	case REQUESTED_SENDING;
+	case SENT;
 }
diff --git a/src/plugin/PluginDescription.php b/src/plugin/PluginDescription.php
index 9fc562af91f..72f0add7fd5 100644
--- a/src/plugin/PluginDescription.php
+++ b/src/plugin/PluginDescription.php
@@ -203,7 +203,7 @@ private function loadMap(array $plugin) : void{
 			}
 			$this->order = $order;
 		}else{
-			$this->order = PluginEnableOrder::POSTWORLD();
+			$this->order = PluginEnableOrder::POSTWORLD;
 		}
 
 		$this->authors = [];
diff --git a/src/plugin/PluginEnableOrder.php b/src/plugin/PluginEnableOrder.php
index 5e6255a16ee..c942ea33f99 100644
--- a/src/plugin/PluginEnableOrder.php
+++ b/src/plugin/PluginEnableOrder.php
@@ -23,63 +23,48 @@
 
 namespace pocketmine\plugin;
 
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 use function mb_strtolower;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static PluginEnableOrder POSTWORLD()
  * @method static PluginEnableOrder STARTUP()
  */
-final class PluginEnableOrder{
-	use EnumTrait {
-		__construct as Enum___construct;
-		register as Enum_register;
-	}
+enum PluginEnableOrder{
+	use LegacyEnumShimTrait;
 
-	protected static function setup() : void{
-		self::registerAll(
-			new self("startup", ["startup"]),
-			new self("postworld", ["postworld"])
-		);
-	}
-
-	/**
-	 * @var self[]
-	 * @phpstan-var array<string, self>
-	 */
-	private static array $aliasMap = [];
-
-	protected static function register(self $member) : void{
-		self::Enum_register($member);
-		foreach($member->getAliases() as $alias){
-			self::$aliasMap[mb_strtolower($alias)] = $member;
-		}
-	}
+	case STARTUP;
+	case POSTWORLD;
 
 	public static function fromString(string $name) : ?self{
-		self::checkInit();
-		return self::$aliasMap[mb_strtolower($name)] ?? null;
-	}
+		/**
+		 * @var self[]|null $aliasMap
+		 * @phpstan-var array<string, self>|null $aliasMap
+		 */
+		static $aliasMap = null;
 
-	/**
-	 * @param string[] $aliases
-	 * @phpstan-param list<string> $aliases
-	 */
-	private function __construct(
-		string $enumName,
-		private array $aliases
-	){
-		$this->Enum___construct($enumName);
+		if($aliasMap === null){
+			$aliasMap = [];
+			foreach(self::cases() as $case){
+				foreach($case->getAliases() as $alias){
+					$aliasMap[$alias] = $case;
+				}
+			}
+		}
+		return $aliasMap[mb_strtolower($name)] ?? null;
 	}
 
 	/**
 	 * @return string[]
 	 * @phpstan-return list<string>
 	 */
-	public function getAliases() : array{ return $this->aliases; }
+	public function getAliases() : array{
+		return match($this){
+			self::STARTUP => ["startup"],
+			self::POSTWORLD => ["postworld"]
+		};
+	}
 }
diff --git a/src/utils/EnumTrait.php b/src/utils/EnumTrait.php
index ba0a4fcf24d..ee1bd4e3506 100644
--- a/src/utils/EnumTrait.php
+++ b/src/utils/EnumTrait.php
@@ -29,6 +29,9 @@
  *
  * Classes using this trait need to include \@method tags in their class docblock for every enum member.
  * Alternatively, just put \@generate-registry-docblock in the docblock and run tools/generate-registry-annotations.php
+ *
+ * @deprecated Use native PHP 8.1 enums instead. Use {@link LegacyEnumShimTrait} if you need to provide backwards
+ * compatible EnumTrait-like API for migrated enums.
  */
 trait EnumTrait{
 	use RegistryTrait;
diff --git a/src/utils/LegacyEnumShimTrait.php b/src/utils/LegacyEnumShimTrait.php
new file mode 100644
index 00000000000..4bb3587ab93
--- /dev/null
+++ b/src/utils/LegacyEnumShimTrait.php
@@ -0,0 +1,92 @@
+<?php
+
+/*
+ *
+ *  ____            _        _   __  __ _                  __  __ ____
+ * |  _ \ ___   ___| | _____| |_|  \/  (_)_ __   ___      |  \/  |  _ \
+ * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
+ * |  __/ (_) | (__|   <  __/ |_| |  | | | | | |  __/_____| |  | |  __/
+ * |_|   \___/ \___|_|\_\___|\__|_|  |_|_|_| |_|\___|     |_|  |_|_|
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * @author PocketMine Team
+ * @link http://www.pocketmine.net/
+ *
+ *
+ */
+
+declare(strict_types=1);
+
+namespace pocketmine\utils;
+
+use function count;
+use function mb_strtoupper;
+use function spl_object_id;
+
+/**
+ * Offers a shim to make a native enum behave similarly to a legacy {@link EnumTrait} enum.
+ * Used to provide backwards compatibility for enums that have been migrated to native PHP 8.1 enums.
+ *
+ * @deprecated
+ */
+trait LegacyEnumShimTrait{
+
+	/**
+	 * @param mixed[] $arguments
+	 */
+	public static function __callStatic(string $name, array $arguments) : self{
+		if(count($arguments) > 0){
+			throw new \ArgumentCountError("Expected exactly 0 arguments, " . count($arguments) . " passed");
+		}
+		return self::getAll()[mb_strtoupper($name)];
+	}
+
+	/**
+	 * Returns a list of all cases, indexed by name.
+	 *
+	 * @return self[]
+	 * @phpstan-return array<string, self>
+	 */
+	public static function getAll() : array{
+		/** @var array<string, self>|null $result */
+		static $result = null;
+		if($result === null){
+			$result = [];
+			foreach(self::cases() as $case){
+				$result[mb_strtoupper($case->name)] = $case;
+			}
+		}
+		return $result;
+	}
+
+	/**
+	 * Shim for {@link \UnitEnum::name}.
+	 *
+	 * @deprecated Use the native enum's name property instead.
+	 */
+	public function name() : string{
+		return $this->name;
+	}
+
+	/**
+	 * Alias of spl_object_id($this).
+	 *
+	 * @deprecated
+	 */
+	public function id() : int{
+		return spl_object_id($this);
+	}
+
+	/**
+	 * Returns whether the two objects are equivalent.
+	 *
+	 * @deprecated Native enums can be safely compared with ===.
+	 */
+	public function equals(self $other) : bool{
+		return $this === $other;
+	}
+}
diff --git a/src/wizard/SetupWizard.php b/src/wizard/SetupWizard.php
index c9170bd8dda..0f1a99f4b3d 100644
--- a/src/wizard/SetupWizard.php
+++ b/src/wizard/SetupWizard.php
@@ -27,13 +27,13 @@
  */
 namespace pocketmine\wizard;
 
-use pocketmine\data\java\GameModeIdMap;
 use pocketmine\lang\KnownTranslationFactory;
 use pocketmine\lang\Language;
 use pocketmine\lang\LanguageNotFoundException;
 use pocketmine\lang\Translatable;
 use pocketmine\player\GameMode;
 use pocketmine\Server;
+use pocketmine\ServerProperties;
 use pocketmine\utils\Config;
 use pocketmine\utils\Internet;
 use pocketmine\utils\InternetException;
@@ -48,8 +48,11 @@
 use const STDIN;
 
 class SetupWizard{
+	/** @deprecated */
 	public const DEFAULT_NAME = Server::DEFAULT_SERVER_NAME;
+	/** @deprecated */
 	public const DEFAULT_PORT = Server::DEFAULT_PORT_IPV4;
+	/** @deprecated */
 	public const DEFAULT_PLAYERS = Server::DEFAULT_MAX_PLAYERS;
 
 	private Language $lang;
@@ -91,7 +94,7 @@ public function run() : bool{
 
 		//this has to happen here to prevent user avoiding agreeing to license
 		$config = new Config(Path::join($this->dataPath, "server.properties"), Config::PROPERTIES);
-		$config->set("language", $lang);
+		$config->set(ServerProperties::LANGUAGE, $lang);
 		$config->save();
 
 		if(strtolower($this->getInput($this->lang->translate(KnownTranslationFactory::skip_installer()), "n", "y/N")) === "y"){
@@ -101,10 +104,12 @@ public function run() : bool{
 
 		$this->writeLine();
 		$this->welcome();
-		$this->generateBaseConfig();
-		$this->generateUserFiles();
 
-		$this->networkFunctions();
+		$this->generateBaseConfig($config);
+		$this->generateUserFiles($config);
+		$this->networkFunctions($config);
+		$config->save();
+
 		$this->printIpDetails();
 
 		$this->endWizard();
@@ -151,32 +156,33 @@ private function askPort(Translatable $prompt, int $default) : int{
 		}
 	}
 
-	private function generateBaseConfig() : void{
-		$config = new Config(Path::join($this->dataPath, "server.properties"), Config::PROPERTIES);
-
-		$config->set("motd", ($name = $this->getInput($this->lang->translate(KnownTranslationFactory::name_your_server()), self::DEFAULT_NAME)));
-		$config->set("server-name", $name);
+	private function generateBaseConfig(Config $config) : void{
+		$config->set(ServerProperties::MOTD, ($name = $this->getInput($this->lang->translate(KnownTranslationFactory::name_your_server()), Server::DEFAULT_SERVER_NAME)));
 
 		$this->message($this->lang->translate(KnownTranslationFactory::port_warning()));
 
-		$config->set("server-port", $this->askPort(KnownTranslationFactory::server_port_v4(), Server::DEFAULT_PORT_IPV4));
-		$config->set("server-portv6", $this->askPort(KnownTranslationFactory::server_port_v6(), Server::DEFAULT_PORT_IPV6));
+		$config->set(ServerProperties::SERVER_PORT_IPV4, $this->askPort(KnownTranslationFactory::server_port_v4(), Server::DEFAULT_PORT_IPV4));
+		$config->set(ServerProperties::SERVER_PORT_IPV6, $this->askPort(KnownTranslationFactory::server_port_v6(), Server::DEFAULT_PORT_IPV6));
 
 		$this->message($this->lang->translate(KnownTranslationFactory::gamemode_info()));
 
 		do{
-			$gamemode = GameModeIdMap::getInstance()->fromId((int) $this->getInput($this->lang->translate(KnownTranslationFactory::default_gamemode()), (string) GameModeIdMap::getInstance()->toId(GameMode::SURVIVAL())));
+			$input = (int) $this->getInput($this->lang->translate(KnownTranslationFactory::default_gamemode()), "0");
+			$gamemode = match($input){
+				0 => GameMode::SURVIVAL,
+				1 => GameMode::CREATIVE,
+				default => null
+			};
 		}while($gamemode === null);
-		$config->set("gamemode", $gamemode->name());
-
-		$config->set("max-players", (int) $this->getInput($this->lang->translate(KnownTranslationFactory::max_players()), (string) self::DEFAULT_PLAYERS));
+		//TODO: this probably shouldn't use the enum name directly
+		$config->set(ServerProperties::GAME_MODE, $gamemode->name);
 
-		$config->set("view-distance", (int) $this->getInput($this->lang->translate(KnownTranslationFactory::view_distance()), (string) Server::DEFAULT_MAX_VIEW_DISTANCE));
+		$config->set(ServerProperties::MAX_PLAYERS, (int) $this->getInput($this->lang->translate(KnownTranslationFactory::max_players()), (string) Server::DEFAULT_MAX_PLAYERS));
 
-		$config->save();
+		$config->set(ServerProperties::VIEW_DISTANCE, (int) $this->getInput($this->lang->translate(KnownTranslationFactory::view_distance()), (string) Server::DEFAULT_MAX_VIEW_DISTANCE));
 	}
 
-	private function generateUserFiles() : void{
+	private function generateUserFiles(Config $config) : void{
 		$this->message($this->lang->translate(KnownTranslationFactory::op_info()));
 
 		$op = strtolower($this->getInput($this->lang->translate(KnownTranslationFactory::op_who()), ""));
@@ -190,27 +196,22 @@ private function generateUserFiles() : void{
 
 		$this->message($this->lang->translate(KnownTranslationFactory::whitelist_info()));
 
-		$config = new Config(Path::join($this->dataPath, "server.properties"), Config::PROPERTIES);
 		if(strtolower($this->getInput($this->lang->translate(KnownTranslationFactory::whitelist_enable()), "n", "y/N")) === "y"){
 			$this->error($this->lang->translate(KnownTranslationFactory::whitelist_warning()));
-			$config->set("white-list", true);
+			$config->set(ServerProperties::WHITELIST, true);
 		}else{
-			$config->set("white-list", false);
+			$config->set(ServerProperties::WHITELIST, false);
 		}
-		$config->save();
 	}
 
-	private function networkFunctions() : void{
-		$config = new Config(Path::join($this->dataPath, "server.properties"), Config::PROPERTIES);
+	private function networkFunctions(Config $config) : void{
 		$this->error($this->lang->translate(KnownTranslationFactory::query_warning1()));
 		$this->error($this->lang->translate(KnownTranslationFactory::query_warning2()));
 		if(strtolower($this->getInput($this->lang->translate(KnownTranslationFactory::query_disable()), "n", "y/N")) === "y"){
-			$config->set("enable-query", false);
+			$config->set(ServerProperties::ENABLE_QUERY, false);
 		}else{
-			$config->set("enable-query", true);
+			$config->set(ServerProperties::ENABLE_QUERY, true);
 		}
-
-		$config->save();
 	}
 
 	private function printIpDetails() : void{
diff --git a/src/world/World.php b/src/world/World.php
index 1103ae8f9e1..8c8ba82c89d 100644
--- a/src/world/World.php
+++ b/src/world/World.php
@@ -1455,9 +1455,9 @@ public function scheduleDelayedBlockUpdate(Vector3 $pos, int $delay) : void{
 		$this->scheduledBlockUpdateQueue->insert(new Vector3((int) $pos->x, (int) $pos->y, (int) $pos->z), $delay + $this->server->getTick());
 	}
 
-	private function tryAddToNeighbourUpdateQueue(Vector3 $pos) : void{
-		if($this->isInWorld($pos->x, $pos->y, $pos->z)){
-			$hash = World::blockHash($pos->x, $pos->y, $pos->z);
+	private function tryAddToNeighbourUpdateQueue(int $x, int $y, int $z) : void{
+		if($this->isInWorld($x, $y, $z)){
+			$hash = World::blockHash($x, $y, $z);
 			if(!isset($this->neighbourBlockUpdateQueueIndex[$hash])){
 				$this->neighbourBlockUpdateQueue->enqueue($hash);
 				$this->neighbourBlockUpdateQueueIndex[$hash] = true;
@@ -1465,17 +1465,28 @@ private function tryAddToNeighbourUpdateQueue(Vector3 $pos) : void{
 		}
 	}
 
+	/**
+	 * Identical to {@link World::notifyNeighbourBlockUpdate()}, but without the Vector3 requirement. We don't want or
+	 * need Vector3 in the places where this is called.
+	 *
+	 * TODO: make this the primary method in PM6
+	 */
+	private function internalNotifyNeighbourBlockUpdate(int $x, int $y, int $z) : void{
+		$this->tryAddToNeighbourUpdateQueue($x, $y, $z);
+		foreach(Facing::OFFSET as [$dx, $dy, $dz]){
+			$this->tryAddToNeighbourUpdateQueue($x + $dx, $y + $dy, $z + $dz);
+		}
+	}
+
 	/**
 	 * Notify the blocks at and around the position that the block at the position may have changed.
 	 * This will cause onNearbyBlockChange() to be called for these blocks.
+	 * TODO: Accept plain integers in PM6 - the Vector3 requirement is an unnecessary inconvenience
 	 *
 	 * @see Block::onNearbyBlockChange()
 	 */
 	public function notifyNeighbourBlockUpdate(Vector3 $pos) : void{
-		$this->tryAddToNeighbourUpdateQueue($pos);
-		foreach($pos->sides() as $side){
-			$this->tryAddToNeighbourUpdateQueue($side);
-		}
+		$this->internalNotifyNeighbourBlockUpdate($pos->getFloorX(), $pos->getFloorY(), $pos->getFloorZ());
 	}
 
 	/**
@@ -1526,13 +1537,13 @@ public function getCollisionBlocks(AxisAlignedBB $bb, bool $targetFirst = false)
 	 *
 	 * @return AxisAlignedBB[]
 	 */
-	private function getCollisionBoxesForCell(int $x, int $y, int $z) : array{
+	private function getBlockCollisionBoxesForCell(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(Facing::OFFSET as [$dx, $dy, $dz]){
+			$extraBoxes = $this->getBlockAt($x + $dx, $y + $dy, $z + $dz)->getCollisionBoxes();
 			foreach($extraBoxes as $extraBox){
 				if($extraBox->intersectsWith($cellBB)){
 					$boxes[] = $extraBox;
@@ -1547,7 +1558,7 @@ private function getCollisionBoxesForCell(int $x, int $y, int $z) : array{
 	 * @return AxisAlignedBB[]
 	 * @phpstan-return list<AxisAlignedBB>
 	 */
-	public function getCollisionBoxes(Entity $entity, AxisAlignedBB $bb, bool $entities = true) : array{
+	public function getBlockCollisionBoxes(AxisAlignedBB $bb) : array{
 		$minX = (int) floor($bb->minX);
 		$minY = (int) floor($bb->minY);
 		$minZ = (int) floor($bb->minZ);
@@ -1563,7 +1574,7 @@ public function getCollisionBoxes(Entity $entity, AxisAlignedBB $bb, bool $entit
 				for($y = $minY; $y <= $maxY; ++$y){
 					$relativeBlockHash = World::chunkBlockHash($x, $y, $z);
 
-					$boxes = $this->blockCollisionBoxCache[$chunkPosHash][$relativeBlockHash] ??= $this->getCollisionBoxesForCell($x, $y, $z);
+					$boxes = $this->blockCollisionBoxCache[$chunkPosHash][$relativeBlockHash] ??= $this->getBlockCollisionBoxesForCell($x, $y, $z);
 
 					foreach($boxes as $blockBB){
 						if($blockBB->intersectsWith($bb)){
@@ -1574,6 +1585,19 @@ public function getCollisionBoxes(Entity $entity, AxisAlignedBB $bb, bool $entit
 			}
 		}
 
+		return $collides;
+	}
+
+	/**
+	 * @deprecated Use {@link World::getBlockCollisionBoxes()} instead (alongside {@link World::getCollidingEntities()}
+	 * if entity collision boxes are also required).
+	 *
+	 * @return AxisAlignedBB[]
+	 * @phpstan-return list<AxisAlignedBB>
+	 */
+	public function getCollisionBoxes(Entity $entity, AxisAlignedBB $bb, bool $entities = true) : array{
+		$collides = $this->getBlockCollisionBoxes($bb);
+
 		if($entities){
 			foreach($this->getCollidingEntities($bb->expandedCopy(0.25, 0.25, 0.25), $entity) as $ent){
 				$collides[] = clone $ent->boundingBox;
@@ -1930,7 +1954,7 @@ public function setBlockAt(int $x, int $y, int $z, Block $block, bool $update =
 
 		if($update){
 			$this->updateAllLight($x, $y, $z);
-			$this->notifyNeighbourBlockUpdate($pos);
+			$this->internalNotifyNeighbourBlockUpdate($x, $y, $z);
 		}
 
 		$this->timings->setBlock->stopTiming();
@@ -2127,8 +2151,8 @@ public function useItemOn(Vector3 $vector, Item &$item, int $face, ?Vector3 $cli
 				}
 
 				$result = $item->onInteractBlock($player, $blockReplace, $blockClicked, $face, $clickVector, $returnedItems);
-				if(!$result->equals(ItemUseResult::NONE())){
-					return $result->equals(ItemUseResult::SUCCESS());
+				if($result !== ItemUseResult::NONE){
+					return $result === ItemUseResult::SUCCESS;
 				}
 			}else{
 				return false;
diff --git a/src/world/biome/BiomeRegistry.php b/src/world/biome/BiomeRegistry.php
index 64818420952..1af60fcd592 100644
--- a/src/world/biome/BiomeRegistry.php
+++ b/src/world/biome/BiomeRegistry.php
@@ -54,7 +54,7 @@ public function __construct(){
 
 		$this->register(BiomeIds::EXTREME_HILLS_EDGE, new SmallMountainsBiome());
 
-		$this->register(BiomeIds::BIRCH_FOREST, new ForestBiome(TreeType::BIRCH()));
+		$this->register(BiomeIds::BIRCH_FOREST, new ForestBiome(TreeType::BIRCH));
 	}
 
 	public function register(int $id, Biome $biome) : void{
diff --git a/src/world/biome/ForestBiome.php b/src/world/biome/ForestBiome.php
index 8f80cb474af..94d3e567ccf 100644
--- a/src/world/biome/ForestBiome.php
+++ b/src/world/biome/ForestBiome.php
@@ -33,7 +33,7 @@ class ForestBiome extends GrassyBiome{
 	public function __construct(?TreeType $type = null){
 		parent::__construct();
 
-		$this->type = $type ?? TreeType::OAK();
+		$this->type = $type ?? TreeType::OAK;
 
 		$trees = new Tree($type);
 		$trees->setBaseAmount(5);
@@ -46,7 +46,7 @@ public function __construct(?TreeType $type = null){
 
 		$this->setElevation(63, 81);
 
-		if($this->type->equals(TreeType::BIRCH())){
+		if($this->type === TreeType::BIRCH){
 			$this->temperature = 0.6;
 			$this->rainfall = 0.5;
 		}else{
diff --git a/src/world/biome/TaigaBiome.php b/src/world/biome/TaigaBiome.php
index cd68c974867..979ca66ec25 100644
--- a/src/world/biome/TaigaBiome.php
+++ b/src/world/biome/TaigaBiome.php
@@ -32,7 +32,7 @@ class TaigaBiome extends SnowyBiome{
 	public function __construct(){
 		parent::__construct();
 
-		$trees = new Tree(TreeType::SPRUCE());
+		$trees = new Tree(TreeType::SPRUCE);
 		$trees->setBaseAmount(10);
 		$this->addPopulator($trees);
 
diff --git a/src/world/generator/object/TreeFactory.php b/src/world/generator/object/TreeFactory.php
index 1d95a77b192..d1f3dbebb35 100644
--- a/src/world/generator/object/TreeFactory.php
+++ b/src/world/generator/object/TreeFactory.php
@@ -31,27 +31,13 @@ final class TreeFactory{
 	 * @param TreeType|null $type default oak
 	 */
 	public static function get(Random $random, ?TreeType $type = null) : ?Tree{
-		$type = $type ?? TreeType::OAK();
-		if($type->equals(TreeType::SPRUCE())){
-			return new SpruceTree();
-		}elseif($type->equals(TreeType::BIRCH())){
-			if($random->nextBoundedInt(39) === 0){
-				return new BirchTree(true);
-			}else{
-				return new BirchTree();
-			}
-		}elseif($type->equals(TreeType::JUNGLE())){
-			return new JungleTree();
-		}elseif($type->equals(TreeType::OAK())){ //default
-			return new OakTree();
-			/*if($random->nextRange(0, 9) === 0){
-				$tree = new BigTree();
-			}else{*/
-
-			//}
-		}elseif($type->equals(TreeType::ACACIA())){
-			return new AcaciaTree();
-		}
-		return null;
+		return match($type){
+			null, TreeType::OAK => new OakTree(), //TODO: big oak has a 1/10 chance
+			TreeType::SPRUCE => new SpruceTree(),
+			TreeType::JUNGLE => new JungleTree(),
+			TreeType::ACACIA => new AcaciaTree(),
+			TreeType::BIRCH => new BirchTree($random->nextBoundedInt(39) === 0),
+			default => null,
+		};
 	}
 }
diff --git a/src/world/generator/object/TreeType.php b/src/world/generator/object/TreeType.php
index 1e8bf56e9a9..d4d62946a5a 100644
--- a/src/world/generator/object/TreeType.php
+++ b/src/world/generator/object/TreeType.php
@@ -23,13 +23,11 @@
 
 namespace pocketmine\world\generator\object;
 
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static TreeType ACACIA()
  * @method static TreeType BIRCH()
@@ -38,34 +36,27 @@
  * @method static TreeType OAK()
  * @method static TreeType SPRUCE()
  */
-final class TreeType{
-	use EnumTrait {
-		register as Enum_register;
-		__construct as Enum___construct;
-	}
-
-	protected static function setup() : void{
-		self::registerAll(
-			new TreeType("oak", "Oak"),
-			new TreeType("spruce", "Spruce"),
-			new TreeType("birch", "Birch"),
-			new TreeType("jungle", "Jungle"),
-			new TreeType("acacia", "Acacia"),
-			new TreeType("dark_oak", "Dark Oak"),
-			//TODO: cherry blossom, mangrove, azalea
-			//TODO: do crimson and warped "trees" belong here? I'm not sure if they're actually trees or just fungi
-			//TODO: perhaps huge mushrooms should be here too???
-		);
-	}
-
-	private function __construct(
-		string $enumName,
-		private string $displayName
-	){
-		$this->Enum___construct($enumName);
-	}
+enum TreeType{
+	use LegacyEnumShimTrait;
+
+	case OAK;
+	case SPRUCE;
+	case BIRCH;
+	case JUNGLE;
+	case ACACIA;
+	case DARK_OAK;
+	//TODO: cherry blossom, mangrove, azalea
+	//TODO: do crimson and warped "trees" belong here? I'm not sure if they're actually trees or just fungi
+	//TODO: perhaps huge mushrooms should be here too???
 
 	public function getDisplayName() : string{
-		return $this->displayName;
+		return match($this){
+			self::OAK => "Oak",
+			self::SPRUCE => "Spruce",
+			self::BIRCH => "Birch",
+			self::JUNGLE => "Jungle",
+			self::ACACIA => "Acacia",
+			self::DARK_OAK => "Dark Oak",
+		};
 	}
 }
diff --git a/src/world/generator/populator/Tree.php b/src/world/generator/populator/Tree.php
index 3e5aef0a777..e470c46f0bd 100644
--- a/src/world/generator/populator/Tree.php
+++ b/src/world/generator/populator/Tree.php
@@ -40,7 +40,7 @@ class Tree implements Populator{
 	 * @param TreeType|null $type default oak
 	 */
 	public function __construct(?TreeType $type = null){
-		$this->type = $type ?? TreeType::OAK();
+		$this->type = $type ?? TreeType::OAK;
 	}
 
 	public function setRandomAmount(int $amount) : void{
diff --git a/src/world/light/LightPropagationContext.php b/src/world/light/LightPropagationContext.php
index 637535344e4..c3d45b3c63d 100644
--- a/src/world/light/LightPropagationContext.php
+++ b/src/world/light/LightPropagationContext.php
@@ -28,8 +28,8 @@ final class LightPropagationContext{
 	/** @phpstan-var \SplQueue<array{int, int, int}> */
 	public \SplQueue $spreadQueue;
 	/**
-	 * @var true[]
-	 * @phpstan-var array<int, true>
+	 * @var int[]|true[]
+	 * @phpstan-var array<int, int|true>
 	 */
 	public array $spreadVisited = [];
 
diff --git a/src/world/light/LightUpdate.php b/src/world/light/LightUpdate.php
index 58ac0a9dcb2..b7455c6cc3c 100644
--- a/src/world/light/LightUpdate.php
+++ b/src/world/light/LightUpdate.php
@@ -137,6 +137,7 @@ public function execute() : int{
 		while(!$context->spreadQueue->isEmpty()){
 			$touched++;
 			[$x, $y, $z] = $context->spreadQueue->dequeue();
+			$from = $context->spreadVisited[World::blockHash($x, $y, $z)];
 
 			unset($context->spreadVisited[World::blockHash($x, $y, $z)]);
 
@@ -155,7 +156,11 @@ public function execute() : int{
 				continue;
 			}
 
-			foreach(Facing::OFFSET as [$ox, $oy, $oz]){
+			foreach(Facing::OFFSET as $side => [$ox, $oy, $oz]){
+				if($from === $side){
+					//don't check the side that this node received its initial light from
+					continue;
+				}
 				$cx = $x + $ox;
 				$cy = $y + $oy;
 				$cz = $z + $oz;
@@ -169,7 +174,7 @@ public function execute() : int{
 					$lightArray = $this->getCurrentLightArray();
 				}
 				assert($subChunk !== null);
-				$this->computeSpreadLight($cx, $cy, $cz, $newAdjacentLight, $context, $lightArray, $subChunk);
+				$this->computeSpreadLight($cx, $cy, $cz, $newAdjacentLight, $context, $lightArray, $subChunk, $side);
 			}
 		}
 
@@ -199,7 +204,7 @@ protected function computeRemoveLight(int $x, int $y, int $z, int $oldAdjacentLe
 		}
 	}
 
-	protected function computeSpreadLight(int $x, int $y, int $z, int $newAdjacentLevel, LightPropagationContext $context, LightArray $lightArray, SubChunk $subChunk) : void{
+	protected function computeSpreadLight(int $x, int $y, int $z, int $newAdjacentLevel, LightPropagationContext $context, LightArray $lightArray, SubChunk $subChunk, int $side) : void{
 		$lx = $x & SubChunk::COORD_MASK;
 		$ly = $y & SubChunk::COORD_MASK;
 		$lz = $z & SubChunk::COORD_MASK;
@@ -210,7 +215,11 @@ protected function computeSpreadLight(int $x, int $y, int $z, int $newAdjacentLe
 			$lightArray->set($lx, $ly, $lz, $potentialLight);
 
 			if(!isset($context->spreadVisited[$index = World::blockHash($x, $y, $z)]) && $potentialLight > 1){
-				$context->spreadVisited[$index] = true;
+				//Track where this node was lit from, to avoid checking the source again when we propagate from here
+				//TODO: In the future it might be worth tracking more than one adjacent source face in case multiple
+				//nodes try to light the same node. However, this is a rare case since the vast majority of calls are
+				//basic propagation with only one source anyway.
+				$context->spreadVisited[$index] = Facing::opposite($side);
 				$context->spreadQueue->enqueue([$x, $y, $z]);
 			}
 		}
diff --git a/src/world/sound/NoteInstrument.php b/src/world/sound/NoteInstrument.php
index 87e5d496d9a..a91d52cf700 100644
--- a/src/world/sound/NoteInstrument.php
+++ b/src/world/sound/NoteInstrument.php
@@ -23,13 +23,11 @@
 
 namespace pocketmine\world\sound;
 
-use pocketmine\utils\EnumTrait;
+use pocketmine\utils\LegacyEnumShimTrait;
 
 /**
- * This doc-block is generated automatically, do not modify it manually.
- * This must be regenerated whenever registry members are added, removed or changed.
- * @see build/generate-registry-annotations.php
- * @generate-registry-docblock
+ * TODO: These tags need to be removed once we get rid of LegacyEnumShimTrait (PM6)
+ *  These are retained for backwards compatibility only.
  *
  * @method static NoteInstrument BANJO()
  * @method static NoteInstrument BASS_DRUM()
@@ -48,27 +46,23 @@
  * @method static NoteInstrument SNARE()
  * @method static NoteInstrument XYLOPHONE()
  */
-final class NoteInstrument{
-	use EnumTrait;
+enum NoteInstrument{
+	use LegacyEnumShimTrait;
 
-	protected static function setup() : void{
-		self::registerAll(
-			new self("piano"),
-			new self("bass_drum"),
-			new self("snare"),
-			new self("clicks_and_sticks"),
-			new self("double_bass"),
-			new self("bell"),
-			new self("flute"),
-			new self("chime"),
-			new self("guitar"),
-			new self("xylophone"),
-			new self("iron_xylophone"),
-			new self("cow_bell"),
-			new self("didgeridoo"),
-			new self("bit"),
-			new self("banjo"),
-			new self("pling")
-		);
-	}
+	case PIANO;
+	case BASS_DRUM;
+	case SNARE;
+	case CLICKS_AND_STICKS;
+	case DOUBLE_BASS;
+	case BELL;
+	case FLUTE;
+	case CHIME;
+	case GUITAR;
+	case XYLOPHONE;
+	case IRON_XYLOPHONE;
+	case COW_BELL;
+	case DIDGERIDOO;
+	case BIT;
+	case BANJO;
+	case PLING;
 }


Test patch:
diff --git a/tests/phpstan/configs/actual-problems.neon b/tests/phpstan/configs/actual-problems.neon
index 070858e8042..cf7e9b02f9a 100644
--- a/tests/phpstan/configs/actual-problems.neon
+++ b/tests/phpstan/configs/actual-problems.neon
@@ -45,11 +45,26 @@ parameters:
 			count: 1
 			path: ../../../src/VersionInfo.php
 
+		-
+			message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
+			count: 3
+			path: ../../../src/block/Block.php
+
+		-
+			message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
+			count: 3
+			path: ../../../src/block/Block.php
+
 		-
 			message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\format\\\\Chunk\\:\\:setBlockStateId\\(\\) expects int, float\\|int given\\.$#"
 			count: 1
 			path: ../../../src/block/Block.php
 
+		-
+			message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
+			count: 3
+			path: ../../../src/block/Block.php
+
 		-
 			message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
 			count: 1
@@ -140,21 +155,6 @@ parameters:
 			count: 1
 			path: ../../../src/block/DragonEgg.php
 
-		-
-			message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
-			count: 1
-			path: ../../../src/block/Farmland.php
-
-		-
-			message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
-			count: 1
-			path: ../../../src/block/Farmland.php
-
-		-
-			message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
-			count: 1
-			path: ../../../src/block/Farmland.php
-
 		-
 			message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
 			count: 1
@@ -285,31 +285,16 @@ parameters:
 			count: 1
 			path: ../../../src/block/Leaves.php
 
-		-
-			message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
-			count: 11
-			path: ../../../src/block/Liquid.php
-
 		-
 			message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#"
 			count: 1
 			path: ../../../src/block/Liquid.php
 
-		-
-			message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
-			count: 11
-			path: ../../../src/block/Liquid.php
-
 		-
 			message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#"
 			count: 1
 			path: ../../../src/block/Liquid.php
 
-		-
-			message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#"
-			count: 11
-			path: ../../../src/block/Liquid.php
-
 		-
 			message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#"
 			count: 1
@@ -937,12 +922,12 @@ parameters:
 
 		-
 			message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#"
-			count: 3
+			count: 2
 			path: ../../../src/world/World.php
 
 		-
 			message: "#^Parameter \\#1 \\$x of static method pocketmine\\\\world\\\\World\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#"
-			count: 3
+			count: 2
 			path: ../../../src/world/World.php
 
 		-
@@ -967,12 +952,12 @@ parameters:
 
 		-
 			message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#"
-			count: 3
+			count: 2
 			path: ../../../src/world/World.php
 
 		-
 			message: "#^Parameter \\#2 \\$y of static method pocketmine\\\\world\\\\World\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#"
-			count: 3
+			count: 2
 			path: ../../../src/world/World.php
 
 		-
@@ -1002,12 +987,12 @@ parameters:
 
 		-
 			message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#"
-			count: 3
+			count: 2
 			path: ../../../src/world/World.php
 
 		-
 			message: "#^Parameter \\#3 \\$z of static method pocketmine\\\\world\\\\World\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#"
-			count: 3
+			count: 2
 			path: ../../../src/world/World.php
 
 		-
diff --git a/tests/phpstan/rules/DeprecatedLegacyEnumAccessRule.php b/tests/phpstan/rules/DeprecatedLegacyEnumAccessRule.php
new file mode 100644
index 00000000000..4fa76702247
--- /dev/null
+++ b/tests/phpstan/rules/DeprecatedLegacyEnumAccessRule.php
@@ -0,0 +1,79 @@
+<?php
+
+/*
+ *
+ *  ____            _        _   __  __ _                  __  __ ____
+ * |  _ \ ___   ___| | _____| |_|  \/  (_)_ __   ___      |  \/  |  _ \
+ * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
+ * |  __/ (_) | (__|   <  __/ |_| |  | | | | | |  __/_____| |  | |  __/
+ * |_|   \___/ \___|_|\_\___|\__|_|  |_|_|_| |_|\___|     |_|  |_|_|
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * @author PocketMine Team
+ * @link http://www.pocketmine.net/
+ *
+ *
+ */
+
+declare(strict_types=1);
+
+namespace pocketmine\phpstan\rules;
+
+use PhpParser\Node;
+use PhpParser\Node\Expr\StaticCall;
+use PHPStan\Analyser\Scope;
+use PHPStan\Rules\Rule;
+use PHPStan\Rules\RuleErrorBuilder;
+use PHPStan\Type\TypeWithClassName;
+use pocketmine\utils\LegacyEnumShimTrait;
+use function sprintf;
+
+/**
+ * @phpstan-implements Rule<StaticCall>
+ */
+final class DeprecatedLegacyEnumAccessRule implements Rule{
+
+	public function getNodeType() : string{
+		return StaticCall::class;
+	}
+
+	public function processNode(Node $node, Scope $scope) : array{
+		/** @var StaticCall $node */
+		if(!$node->name instanceof Node\Identifier){
+			return [];
+		}
+		$caseName = $node->name->name;
+		$classType = $node->class instanceof Node\Name ?
+			$scope->resolveTypeByName($node->class) :
+			$scope->getType($node->class);
+
+		if(!$classType instanceof TypeWithClassName){
+			return [];
+		}
+
+		$reflection = $classType->getClassReflection();
+		if($reflection === null || !$reflection->hasTraitUse(LegacyEnumShimTrait::class) || !$reflection->implementsInterface(\UnitEnum::class)){
+			return [];
+		}
+
+		if(!$reflection->hasNativeMethod($caseName)){
+			return [
+				RuleErrorBuilder::message(sprintf(
+					'Use of legacy enum case accessor %s::%s().',
+					$reflection->getName(),
+					$caseName
+				))->tip(sprintf(
+					'Access the enum constant directly instead (remove the brackets), e.g. %s::%s',
+					$reflection->getName(),
+					$caseName
+				))->build()
+			];
+		}
+
+		return [];
+	}
+}
diff --git a/tests/phpunit/block/BlockTest.php b/tests/phpunit/block/BlockTest.php
index 3b84b8021f5..f876bf402d4 100644
--- a/tests/phpunit/block/BlockTest.php
+++ b/tests/phpunit/block/BlockTest.php
@@ -28,6 +28,7 @@
 use function file_get_contents;
 use function is_array;
 use function json_decode;
+use function log;
 use function print_r;
 use const SORT_STRING;
 
@@ -125,6 +126,14 @@ public function testEmptyStateId() : void{
 		self::assertInstanceOf(Air::class, $block);
 	}
 
+	public function testStateDataSizeNotTooLarge() : void{
+		$typeIdBitsMin = ((int) log(BlockTypeIds::FIRST_UNUSED_BLOCK_ID, 2)) + 1;
+
+		$typeIdBitsMin++; //for custom blocks
+
+		self::assertLessThanOrEqual(32, Block::INTERNAL_STATE_DATA_BITS + $typeIdBitsMin, "State data size cannot be larger than " . (32 - $typeIdBitsMin) . " bits (need at least $typeIdBitsMin bits for block type ID)");
+	}
+
 	public function testAsItemFromItem() : void{
 		$block = VanillaBlocks::FLOWER_POT();
 		$item = $block->asItem();
diff --git a/tests/phpunit/block/BrewingStandTest.php b/tests/phpunit/block/BrewingStandTest.php
index b3363b98cd3..85cdd90e113 100644
--- a/tests/phpunit/block/BrewingStandTest.php
+++ b/tests/phpunit/block/BrewingStandTest.php
@@ -25,7 +25,6 @@
 
 use PHPUnit\Framework\TestCase;
 use pocketmine\block\utils\BrewingStandSlot;
-use function array_values;
 use function count;
 
 class BrewingStandTest extends TestCase{
@@ -34,9 +33,9 @@ class BrewingStandTest extends TestCase{
 	 * @phpstan-return \Generator<int, array{list<BrewingStandSlot>}, void, void>
 	 */
 	public static function slotsProvider() : \Generator{
-		yield [array_values(BrewingStandSlot::getAll())];
-		yield [[BrewingStandSlot::EAST()]];
-		yield [[BrewingStandSlot::EAST(), BrewingStandSlot::NORTHWEST()]];
+		yield [BrewingStandSlot::cases()];
+		yield [[BrewingStandSlot::EAST]];
+		yield [[BrewingStandSlot::EAST, BrewingStandSlot::NORTHWEST]];
 	}
 
 	/**
diff --git a/tests/phpunit/block/block_factory_consistency_check.json b/tests/phpunit/block/block_factory_consistency_check.json
index c120105649e..9e68daf0c4b 100644
--- a/tests/phpunit/block/block_factory_consistency_check.json
+++ b/tests/phpunit/block/block_factory_consistency_check.json
@@ -1,1 +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 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
+{"knownStates":{"???":[20992881],"Acacia Button":[20482064,20482065,20482068,20482069,20482070,20482071,20482072,20482073,20482076,20482077,20482078,20482079],"Acacia Door":[20484288,20484289,20484290,20484291,20484292,20484293,20484294,20484295,20484296,20484297,20484298,20484299,20484300,20484301,20484302,20484303,20484304,20484305,20484306,20484307,20484308,20484309,20484310,20484311,20484312,20484313,20484314,20484315,20484316,20484317,20484318,20484319],"Acacia Fence":[20487161],"Acacia Fence Gate":[20489840,20489841,20489842,20489843,20489844,20489845,20489846,20489847,20489848,20489849,20489850,20489851,20489852,20489853,20489854,20489855],"Acacia Leaves":[20490916,20490917,20490918,20490919],"Acacia Log":[20492840,20492841,20492842,20492843,20492846,20492847],"Acacia Planks":[20496170],"Acacia Pressure Plate":[20497912,20497913],"Acacia Sapling":[20499538,20499539],"Acacia Sign":[20502272,20502273,20502274,20502275,20502276,20502277,20502278,20502279,20502280,20502281,20502282,20502283,20502284,20502285,20502286,20502287],"Acacia Slab":[20502804,20502805,20502806],"Acacia Stairs":[20504680,20504681,20504682,20504683,20504684,20504685,20504686,20504687],"Acacia Trapdoor":[20508544,20508545,20508546,20508547,20508548,20508549,20508550,20508551,20508552,20508553,20508554,20508555,20508556,20508557,20508558,20508559],"Acacia Wall Sign":[20508856,20508857,20508858,20508859],"Acacia Wood":[20511120,20511121,20511122,20511123,20511124,20511125],"Actinium":[20754337],"Activator Rail":[20513778,20513779,20513780,20513781,20513782,20513783,20513786,20513787,20513788,20513789,20513790,20513791],"Air":[20481500],"All Sided Mushroom Stem":[20515945],"Allium":[20517436],"Aluminum":[20756409],"Americium":[20756900],"Amethyst":[21587565],"Amethyst Cluster":[21582624,21582625,21582626,21582627,21582628,21582629,21582630,21582631,21582632,21582633,21582634,21582635,21582636,21582637,21582638,21582639,21582640,21582641,21582642,21582643,21582644,21582645,21582646,21582647],"Ancient Debris":[21589690],"Andesite":[20520193],"Andesite Slab":[20521816,20521818,20521819],"Andesite Stairs":[20523416,20523417,20523418,20523419,20523420,20523421,20523422,20523423],"Andesite Wall":[20525824,20525825,20525826,20525827,20525828,20525829,20525830,20525831,20525832,20525833,20525834,20525835,20525836,20525837,20525838,20525839,20525846,20525888,20525889,20525890,20525891,20525892,20525893,20525894,20525895,20525896,20525897,20525898,20525899,20525900,20525901,20525902,20525903,20525904,20525905,20525906,20525907,20525908,20525909,20525910,20525911,20525912,20525913,20525914,20525915,20525916,20525917,20525918,20525919,20525920,20525921,20525922,20525923,20525924,20525925,20525926,20525927,20525928,20525929,20525930,20525931,20525932,20525933,20525934,20525935,20525936,20525937,20525938,20525939,20525940,20525941,20525942,20525943,20525944,20525945,20525946,20525947,20525948,20525949,20525950,20525951,20525952,20525953,20525954,20525955,20525956,20525957,20525958,20525959,20525960,20525961,20525962,20525963,20525964,20525965,20525966,20525967,20525974,20526016,20526017,20526018,20526019,20526020,20526021,20526022,20526023,20526024,20526025,20526026,20526027,20526028,20526029,20526030,20526031,20526032,20526033,20526034,20526035,20526036,20526037,20526038,20526039,20526040,20526041,20526042,20526043,20526044,20526045,20526046,20526047,20526048,20526049,20526050,20526051,20526052,20526053,20526054,20526055,20526056,20526057,20526058,20526059,20526060,20526061,20526062,20526063,20526064,20526065,20526066,20526067,20526068,20526069,20526070,20526071,20526072,20526073,20526074,20526075,20526076,20526077,20526078,20526079],"Antimony":[20760253],"Anvil":[20528176,20528177,20528179,20528180,20528181,20528183,20528184,20528185,20528187,20528188,20528189,20528191],"Argon":[20760826],"Arsenic":[20763178],"Astatine":[20766501],"Azalea Leaves":[21886340,21886341,21886342,21886343],"Azure Bluet":[20531031],"Bamboo":[20531777,20531778,20531779,20531781,20531782,20531783,20531785,20531786,20531787,20531789,20531790,20531791],"Bamboo Sapling":[20533516,20533517],"Banner":[20535808,20535809,20535810,20535811,20535812,20535813,20535814,20535815,20535816,20535817,20535818,20535819,20535820,20535821,20535822,20535823,20535824,20535825,20535826,20535827,20535828,20535829,20535830,20535831,20535832,20535833,20535834,20535835,20535836,20535837,20535838,20535839,20535840,20535841,20535842,20535843,20535844,20535845,20535846,20535847,20535848,20535849,20535850,20535851,20535852,20535853,20535854,20535855,20535856,20535857,20535858,20535859,20535860,20535861,20535862,20535863,20535864,20535865,20535866,20535867,20535868,20535869,20535870,20535871,20535872,20535873,20535874,20535875,20535876,20535877,20535878,20535879,20535880,20535881,20535882,20535883,20535884,20535885,20535886,20535887,20535888,20535889,20535890,20535891,20535892,20535893,20535894,20535895,20535896,20535897,20535898,20535899,20535900,20535901,20535902,20535903,20535904,20535905,20535906,20535907,20535908,20535909,20535910,20535911,20535912,20535913,20535914,20535915,20535916,20535917,20535918,20535919,20535920,20535921,20535922,20535923,20535924,20535925,20535926,20535927,20535928,20535929,20535930,20535931,20535932,20535933,20535934,20535935,20535936,20535937,20535938,20535939,20535940,20535941,20535942,20535943,20535944,20535945,20535946,20535947,20535948,20535949,20535950,20535951,20535952,20535953,20535954,20535955,20535956,20535957,20535958,20535959,20535960,20535961,20535962,20535963,20535964,20535965,20535966,20535967,20535968,20535969,20535970,20535971,20535972,20535973,20535974,20535975,20535976,20535977,20535978,20535979,20535980,20535981,20535982,20535983,20535984,20535985,20535986,20535987,20535988,20535989,20535990,20535991,20535992,20535993,20535994,20535995,20535996,20535997,20535998,20535999,20536000,20536001,20536002,20536003,20536004,20536005,20536006,20536007,20536008,20536009,20536010,20536011,20536012,20536013,20536014,20536015,20536016,20536017,20536018,20536019,20536020,20536021,20536022,20536023,20536024,20536025,20536026,20536027,20536028,20536029,20536030,20536031,20536032,20536033,20536034,20536035,20536036,20536037,20536038,20536039,20536040,20536041,20536042,20536043,20536044,20536045,20536046,20536047,20536048,20536049,20536050,20536051,20536052,20536053,20536054,20536055,20536056,20536057,20536058,20536059,20536060,20536061,20536062,20536063],"Barium":[20767788],"Barrel":[20538288,20538289,20538290,20538291,20538294,20538295,20538296,20538297,20538298,20538299,20538302,20538303],"Barrier":[20541302],"Basalt":[21591048,21591049,21591050],"Beacon":[20541801],"Bed Block":[20545280,20545281,20545282,20545283,20545284,20545285,20545286,20545287,20545288,20545289,20545290,20545291,20545292,20545293,20545294,20545295,20545296,20545297,20545298,20545299,20545300,20545301,20545302,20545303,20545304,20545305,20545306,20545307,20545308,20545309,20545310,20545311,20545312,20545313,20545314,20545315,20545316,20545317,20545318,20545319,20545320,20545321,20545322,20545323,20545324,20545325,20545326,20545327,20545328,20545329,20545330,20545331,20545332,20545333,20545334,20545335,20545336,20545337,20545338,20545339,20545340,20545341,20545342,20545343,20545344,20545345,20545346,20545347,20545348,20545349,20545350,20545351,20545352,20545353,20545354,20545355,20545356,20545357,20545358,20545359,20545360,20545361,20545362,20545363,20545364,20545365,20545366,20545367,20545368,20545369,20545370,20545371,20545372,20545373,20545374,20545375,20545376,20545377,20545378,20545379,20545380,20545381,20545382,20545383,20545384,20545385,20545386,20545387,20545388,20545389,20545390,20545391,20545392,20545393,20545394,20545395,20545396,20545397,20545398,20545399,20545400,20545401,20545402,20545403,20545404,20545405,20545406,20545407,20545408,20545409,20545410,20545411,20545412,20545413,20545414,20545415,20545416,20545417,20545418,20545419,20545420,20545421,20545422,20545423,20545424,20545425,20545426,20545427,20545428,20545429,20545430,20545431,20545432,20545433,20545434,20545435,20545436,20545437,20545438,20545439,20545440,20545441,20545442,20545443,20545444,20545445,20545446,20545447,20545448,20545449,20545450,20545451,20545452,20545453,20545454,20545455,20545456,20545457,20545458,20545459,20545460,20545461,20545462,20545463,20545464,20545465,20545466,20545467,20545468,20545469,20545470,20545471,20545472,20545473,20545474,20545475,20545476,20545477,20545478,20545479,20545480,20545481,20545482,20545483,20545484,20545485,20545486,20545487,20545488,20545489,20545490,20545491,20545492,20545493,20545494,20545495,20545496,20545497,20545498,20545499,20545500,20545501,20545502,20545503,20545504,20545505,20545506,20545507,20545508,20545509,20545510,20545511,20545512,20545513,20545514,20545515,20545516,20545517,20545518,20545519,20545520,20545521,20545522,20545523,20545524,20545525,20545526,20545527,20545528,20545529,20545530,20545531,20545532,20545533,20545534,20545535],"Bedrock":[20545540,20545541],"Beetroot Block":[20549328,20549329,20549330,20549331,20549332,20549333,20549334,20549335],"Bell":[20551488,20551489,20551490,20551491,20551492,20551493,20551494,20551495,20551496,20551497,20551498,20551499,20551500,20551501,20551502,20551503],"Berkelium":[20768774],"Beryllium":[20771395],"Big Dripleaf":[21929936,21929937,21929938,21929939,21929940,21929941,21929942,21929943,21929944,21929945,21929946,21929947,21929948,21929949,21929950,21929951],"Big Dripleaf Stem":[21931976,21931977,21931978,21931979],"Birch Button":[20552224,20552225,20552226,20552227,20552230,20552231,20552232,20552233,20552234,20552235,20552238,20552239],"Birch Door":[20554848,20554849,20554850,20554851,20554852,20554853,20554854,20554855,20554856,20554857,20554858,20554859,20554860,20554861,20554862,20554863,20554864,20554865,20554866,20554867,20554868,20554869,20554870,20554871,20554872,20554873,20554874,20554875,20554876,20554877,20554878,20554879],"Birch Fence":[20557622],"Birch Fence Gate":[20559792,20559793,20559794,20559795,20559796,20559797,20559798,20559799,20559800,20559801,20559802,20559803,20559804,20559805,20559806,20559807],"Birch Leaves":[20560344,20560345,20560346,20560347],"Birch Log":[20563648,20563649,20563652,20563653,20563654,20563655],"Birch Planks":[20564186],"Birch Pressure Plate":[20567746,20567747],"Birch Sapling":[20568972,20568973],"Birch Sign":[20571536,20571537,20571538,20571539,20571540,20571541,20571542,20571543,20571544,20571545,20571546,20571547,20571548,20571549,20571550,20571551],"Birch Slab":[20573196,20573197,20573198],"Birch Stairs":[20575080,20575081,20575082,20575083,20575084,20575085,20575086,20575087],"Birch Trapdoor":[20577008,20577009,20577010,20577011,20577012,20577013,20577014,20577015,20577016,20577017,20577018,20577019,20577020,20577021,20577022,20577023],"Birch Wall Sign":[20579020,20579021,20579022,20579023],"Birch Wood":[20580826,20580827,20580828,20580829,20580830,20580831],"Bismuth":[20773935],"Blackstone":[21596307],"Blackstone Slab":[21599012,21599014,21599015],"Blackstone Stairs":[21601800,21601801,21601802,21601803,21601804,21601805,21601806,21601807],"Blackstone Wall":[21604098,21604112,21604113,21604114,21604115,21604116,21604117,21604118,21604119,21604120,21604121,21604122,21604123,21604124,21604125,21604126,21604127,21604160,21604161,21604162,21604163,21604164,21604165,21604166,21604167,21604168,21604169,21604170,21604171,21604172,21604173,21604174,21604175,21604176,21604177,21604178,21604179,21604180,21604181,21604182,21604183,21604184,21604185,21604186,21604187,21604188,21604189,21604190,21604191,21604192,21604193,21604194,21604195,21604196,21604197,21604198,21604199,21604200,21604201,21604202,21604203,21604204,21604205,21604206,21604207,21604208,21604209,21604210,21604211,21604212,21604213,21604214,21604215,21604216,21604217,21604218,21604219,21604220,21604221,21604222,21604223,21604226,21604240,21604241,21604242,21604243,21604244,21604245,21604246,21604247,21604248,21604249,21604250,21604251,21604252,21604253,21604254,21604255,21604288,21604289,21604290,21604291,21604292,21604293,21604294,21604295,21604296,21604297,21604298,21604299,21604300,21604301,21604302,21604303,21604304,21604305,21604306,21604307,21604308,21604309,21604310,21604311,21604312,21604313,21604314,21604315,21604316,21604317,21604318,21604319,21604320,21604321,21604322,21604323,21604324,21604325,21604326,21604327,21604328,21604329,21604330,21604331,21604332,21604333,21604334,21604335,21604336,21604337,21604338,21604339,21604340,21604341,21604342,21604343,21604344,21604345,21604346,21604347,21604348,21604349,21604350,21604351],"Blast Furnace":[20585328,20585329,20585330,20585331,20585332,20585333,20585334,20585335],"Blue Ice":[20588595],"Blue Orchid":[20591829],"Blue Torch":[20593968,20593969,20593973,20593974,20593975],"Bohrium":[20775552],"Bone Block":[20596340,20596341,20596343],"Bookshelf":[20597864],"Boron":[20777985],"Brewing Stand":[20600176,20600177,20600178,20600179,20600180,20600181,20600182,20600183],"Brick Slab":[20601168,20601169,20601171],"Brick Stairs":[20604408,20604409,20604410,20604411,20604412,20604413,20604414,20604415],"Brick Wall":[20605952,20605953,20605954,20605955,20605956,20605957,20605958,20605959,20605960,20605961,20605962,20605963,20605964,20605965,20605966,20605967,20605968,20605969,20605970,20605971,20605972,20605973,20605974,20605975,20605976,20605977,20605978,20605979,20605980,20605981,20605982,20605983,20605984,20605985,20605986,20605987,20605988,20605989,20605990,20605991,20605992,20605993,20605994,20605995,20605996,20605997,20605998,20605999,20606000,20606001,20606002,20606003,20606004,20606005,20606006,20606007,20606008,20606009,20606010,20606011,20606012,20606013,20606014,20606015,20606053,20606064,20606065,20606066,20606067,20606068,20606069,20606070,20606071,20606072,20606073,20606074,20606075,20606076,20606077,20606078,20606079,20606080,20606081,20606082,20606083,20606084,20606085,20606086,20606087,20606088,20606089,20606090,20606091,20606092,20606093,20606094,20606095,20606096,20606097,20606098,20606099,20606100,20606101,20606102,20606103,20606104,20606105,20606106,20606107,20606108,20606109,20606110,20606111,20606112,20606113,20606114,20606115,20606116,20606117,20606118,20606119,20606120,20606121,20606122,20606123,20606124,20606125,20606126,20606127,20606128,20606129,20606130,20606131,20606132,20606133,20606134,20606135,20606136,20606137,20606138,20606139,20606140,20606141,20606142,20606143,20606181,20606192,20606193,20606194,20606195,20606196,20606197,20606198,20606199,20606200,20606201,20606202,20606203,20606204,20606205,20606206,20606207],"Bricks":[20608594],"Bromine":[20779949],"Brown Mushroom":[20613080],"Brown Mushroom Block":[20614032,20614033,20614034,20614035,20614036,20614037,20614038,20614039,20614044,20614045,20614047],"Budding Amethyst":[21577592],"Cactus":[20616384,20616385,20616386,20616387,20616388,20616389,20616390,20616391,20616392,20616393,20616394,20616395,20616396,20616397,20616398,20616399],"Cadmium":[20781893],"Cake":[20618584,20618585,20618586,20618587,20618588,20618589,20618590],"Cake With Candle":[21837222,21837223],"Cake With Dyed Candle":[21839232,21839233,21839234,21839235,21839236,21839237,21839238,21839239,21839240,21839241,21839242,21839243,21839244,21839245,21839246,21839247,21839248,21839249,21839250,21839251,21839252,21839253,21839254,21839255,21839256,21839257,21839258,21839259,21839260,21839261,21839262,21839263],"Calcite":[21638381],"Calcium":[20783925],"Californium":[20787131],"Candle":[21832176,21832177,21832178,21832179,21832180,21832181,21832182,21832183],"Carbon":[20789232],"Carpet":[20619280,20619281,20619282,20619283,20619284,20619285,20619286,20619287,20619288,20619289,20619290,20619291,20619292,20619293,20619294,20619295],"Carrot Block":[20622752,20622753,20622754,20622755,20622756,20622757,20622758,20622759],"Cartography Table":[21845741],"Carved Pumpkin":[20624628,20624629,20624630,20624631],"Cauldron":[21852969],"Cave Vines":[21892224,21892225,21892226,21892227,21892228,21892229,21892230,21892231,21892232,21892233,21892234,21892235,21892236,21892237,21892238,21892239,21892244,21892245,21892248,21892249,21892250,21892251,21892252,21892253,21892254,21892255,21892256,21892257,21892258,21892259,21892260,21892261,21892262,21892263,21892264,21892265,21892266,21892267,21892268,21892269,21892270,21892271,21892276,21892277,21892280,21892281,21892282,21892283,21892284,21892285,21892286,21892287,21892288,21892289,21892290,21892291,21892292,21892293,21892294,21892295,21892296,21892297,21892298,21892299,21892300,21892301,21892302,21892303,21892308,21892309,21892312,21892313,21892314,21892315,21892316,21892317,21892318,21892319,21892320,21892321,21892322,21892323,21892324,21892325,21892326,21892327,21892328,21892329,21892330,21892331,21892332,21892333,21892334,21892335,21892340,21892341,21892344,21892345,21892346,21892347,21892348,21892349,21892350,21892351],"Cerium":[20789431],"Cesium":[20791798],"Chain":[21878573,21878574,21878575],"Cherry Button":[21895570,21895571,21895572,21895573,21895574,21895575,21895578,21895579,21895580,21895581,21895582,21895583],"Cherry Door":[21898304,21898305,21898306,21898307,21898308,21898309,21898310,21898311,21898312,21898313,21898314,21898315,21898316,21898317,21898318,21898319,21898320,21898321,21898322,21898323,21898324,21898325,21898326,21898327,21898328,21898329,21898330,21898331,21898332,21898333,21898334,21898335],"Cherry Fence":[21900661],"Cherry Fence Gate":[21901904,21901905,21901906,21901907,21901908,21901909,21901910,21901911,21901912,21901913,21901914,21901915,21901916,21901917,21901918,21901919],"Cherry Leaves":[21904412,21904413,21904414,21904415],"Cherry Log":[21905784,21905785,21905786,21905787,21905790,21905791],"Cherry Planks":[21908756],"Cherry Pressure Plate":[21911536,21911537],"Cherry Sign":[21914944,21914945,21914946,21914947,21914948,21914949,21914950,21914951,21914952,21914953,21914954,21914955,21914956,21914957,21914958,21914959],"Cherry Slab":[21917632,21917633,21917635],"Cherry Stairs":[21918912,21918913,21918914,21918915,21918916,21918917,21918918,21918919],"Cherry Trapdoor":[21919968,21919969,21919970,21919971,21919972,21919973,21919974,21919975,21919976,21919977,21919978,21919979,21919980,21919981,21919982,21919983],"Cherry Wall Sign":[21923100,21923101,21923102,21923103],"Cherry Wood":[21924008,21924009,21924012,21924013,21924014,21924015],"Chest":[20628872,20628873,20628874,20628875],"Chiseled Bookshelf":[21938176,21938177,21938178,21938179,21938180,21938181,21938182,21938183,21938184,21938185,21938186,21938187,21938188,21938189,21938190,21938191,21938192,21938193,21938194,21938195,21938196,21938197,21938198,21938199,21938200,21938201,21938202,21938203,21938204,21938205,21938206,21938207,21938208,21938209,21938210,21938211,21938212,21938213,21938214,21938215,21938216,21938217,21938218,21938219,21938220,21938221,21938222,21938223,21938224,21938225,21938226,21938227,21938228,21938229,21938230,21938231,21938232,21938233,21938234,21938235,21938236,21938237,21938238,21938239,21938240,21938241,21938242,21938243,21938244,21938245,21938246,21938247,21938248,21938249,21938250,21938251,21938252,21938253,21938254,21938255,21938256,21938257,21938258,21938259,21938260,21938261,21938262,21938263,21938264,21938265,21938266,21938267,21938268,21938269,21938270,21938271,21938272,21938273,21938274,21938275,21938276,21938277,21938278,21938279,21938280,21938281,21938282,21938283,21938284,21938285,21938286,21938287,21938288,21938289,21938290,21938291,21938292,21938293,21938294,21938295,21938296,21938297,21938298,21938299,21938300,21938301,21938302,21938303,21938304,21938305,21938306,21938307,21938308,21938309,21938310,21938311,21938312,21938313,21938314,21938315,21938316,21938317,21938318,21938319,21938320,21938321,21938322,21938323,21938324,21938325,21938326,21938327,21938328,21938329,21938330,21938331,21938332,21938333,21938334,21938335,21938336,21938337,21938338,21938339,21938340,21938341,21938342,21938343,21938344,21938345,21938346,21938347,21938348,21938349,21938350,21938351,21938352,21938353,21938354,21938355,21938356,21938357,21938358,21938359,21938360,21938361,21938362,21938363,21938364,21938365,21938366,21938367,21938368,21938369,21938370,21938371,21938372,21938373,21938374,21938375,21938376,21938377,21938378,21938379,21938380,21938381,21938382,21938383,21938384,21938385,21938386,21938387,21938388,21938389,21938390,21938391,21938392,21938393,21938394,21938395,21938396,21938397,21938398,21938399,21938400,21938401,21938402,21938403,21938404,21938405,21938406,21938407,21938408,21938409,21938410,21938411,21938412,21938413,21938414,21938415,21938416,21938417,21938418,21938419,21938420,21938421,21938422,21938423,21938424,21938425,21938426,21938427,21938428,21938429,21938430,21938431],"Chiseled Deepslate":[21680290],"Chiseled Nether Bricks":[21682906],"Chiseled Polished Blackstone":[21618038],"Chiseled Quartz Block":[20630148,20630150,20630151],"Chiseled Red Sandstone":[20633244],"Chiseled Sandstone":[20635615],"Chiseled Stone Bricks":[20636034],"Chlorine":[20794258],"Chorus Flower":[21864360,21864361,21864364,21864365,21864366,21864367],"Chorus Plant":[21866123],"Chromium":[20797122],"Clay Block":[20639448],"Coal Block":[20641681],"Coal Ore":[20642280],"Cobalt":[20799311],"Cobbled Deepslate":[21662962],"Cobbled Deepslate Slab":[21665324,21665326,21665327],"Cobbled Deepslate Stairs":[21666232,21666233,21666234,21666235,21666236,21666237,21666238,21666239],"Cobbled Deepslate Wall":[21668389,21668400,21668401,21668402,21668403,21668404,21668405,21668406,21668407,21668408,21668409,21668410,21668411,21668412,21668413,21668414,21668415,21668416,21668417,21668418,21668419,21668420,21668421,21668422,21668423,21668424,21668425,21668426,21668427,21668428,21668429,21668430,21668431,21668432,21668433,21668434,21668435,21668436,21668437,21668438,21668439,21668440,21668441,21668442,21668443,21668444,21668445,21668446,21668447,21668448,21668449,21668450,21668451,21668452,21668453,21668454,21668455,21668456,21668457,21668458,21668459,21668460,21668461,21668462,21668463,21668464,21668465,21668466,21668467,21668468,21668469,21668470,21668471,21668472,21668473,21668474,21668475,21668476,21668477,21668478,21668479,21668517,21668528,21668529,21668530,21668531,21668532,21668533,21668534,21668535,21668536,21668537,21668538,21668539,21668540,21668541,21668542,21668543,21668544,21668545,21668546,21668547,21668548,21668549,21668550,21668551,21668552,21668553,21668554,21668555,21668556,21668557,21668558,21668559,21668560,21668561,21668562,21668563,21668564,21668565,21668566,21668567,21668568,21668569,21668570,21668571,21668572,21668573,21668574,21668575,21668576,21668577,21668578,21668579,21668580,21668581,21668582,21668583,21668584,21668585,21668586,21668587,21668588,21668589,21668590,21668591,21668592,21668593,21668594,21668595,21668596,21668597,21668598,21668599,21668600,21668601,21668602,21668603,21668604,21668605,21668606,21668607],"Cobblestone":[20645646],"Cobblestone Slab":[20647760,20647762,20647763],"Cobblestone Stairs":[20648720,20648721,20648722,20648723,20648724,20648725,20648726,20648727],"Cobblestone Wall":[20649984,20649985,20649986,20649987,20649988,20649989,20649990,20649991,20649992,20649993,20649994,20649995,20649996,20649997,20649998,20649999,20650000,20650001,20650002,20650003,20650004,20650005,20650006,20650007,20650008,20650009,20650010,20650011,20650012,20650013,20650014,20650015,20650016,20650017,20650018,20650019,20650020,20650021,20650022,20650023,20650024,20650025,20650026,20650027,20650028,20650029,20650030,20650031,20650032,20650033,20650034,20650035,20650036,20650037,20650038,20650039,20650040,20650041,20650042,20650043,20650044,20650045,20650046,20650047,20650080,20650081,20650082,20650083,20650084,20650085,20650086,20650087,20650088,20650089,20650090,20650091,20650092,20650093,20650094,20650095,20650099,20650112,20650113,20650114,20650115,20650116,20650117,20650118,20650119,20650120,20650121,20650122,20650123,20650124,20650125,20650126,20650127,20650128,20650129,20650130,20650131,20650132,20650133,20650134,20650135,20650136,20650137,20650138,20650139,20650140,20650141,20650142,20650143,20650144,20650145,20650146,20650147,20650148,20650149,20650150,20650151,20650152,20650153,20650154,20650155,20650156,20650157,20650158,20650159,20650160,20650161,20650162,20650163,20650164,20650165,20650166,20650167,20650168,20650169,20650170,20650171,20650172,20650173,20650174,20650175,20650208,20650209,20650210,20650211,20650212,20650213,20650214,20650215,20650216,20650217,20650218,20650219,20650220,20650221,20650222,20650223,20650227],"Cobweb":[20652464],"Cocoa Block":[20655024,20655025,20655026,20655027,20655028,20655029,20655030,20655031,20655032,20655033,20655034,20655035],"Compound Creator":[20656176,20656177,20656178,20656179],"Concrete":[20659968,20659969,20659970,20659971,20659972,20659973,20659974,20659975,20659976,20659977,20659978,20659979,20659980,20659981,20659982,20659983],"Concrete Powder":[20661296,20661297,20661298,20661299,20661300,20661301,20661302,20661303,20661304,20661305,20661306,20661307,20661308,20661309,20661310,20661311],"Copernicium":[20802812],"Copper":[20804760],"Copper Block":[21823528,21823529,21823530,21823531,21823532,21823533,21823534,21823535],"Copper Ore":[21798993],"Coral":[20663618,20663620,20663621,20663622,20663623,20663626,20663628,20663629,20663630,20663631],"Coral Block":[20664465,20664468,20664469,20664470,20664471,20664473,20664476,20664477,20664478,20664479],"Coral Fan":[20668131,20668132,20668133,20668134,20668135,20668139,20668140,20668141,20668142,20668143,20668147,20668148,20668149,20668150,20668151,20668155,20668156,20668157,20668158,20668159],"Cornflower":[20670352],"Cracked Deepslate Bricks":[21650157],"Cracked Deepslate Tiles":[21661164],"Cracked Nether Bricks":[21685186],"Cracked Polished Blackstone Bricks":[21628426],"Cracked Stone Bricks":[20670706],"Crafting Table":[20672716],"Crimson Button":[21737520,21737521,21737522,21737523,21737526,21737527,21737528,21737529,21737530,21737531,21737534,21737535],"Crimson Door":[21751360,21751361,21751362,21751363,21751364,21751365,21751366,21751367,21751368,21751369,21751370,21751371,21751372,21751373,21751374,21751375,21751376,21751377,21751378,21751379,21751380,21751381,21751382,21751383,21751384,21751385,21751386,21751387,21751388,21751389,21751390,21751391],"Crimson Fence":[21707929],"Crimson Fence Gate":[21757232,21757233,21757234,21757235,21757236,21757237,21757238,21757239,21757240,21757241,21757242,21757243,21757244,21757245,21757246,21757247],"Crimson Hyphae":[21726912,21726913,21726916,21726917,21726918,21726919],"Crimson Planks":[21701319],"Crimson Pressure Plate":[21745172,21745173],"Crimson Roots":[21934477],"Crimson Sign":[21769568,21769569,21769570,21769571,21769572,21769573,21769574,21769575,21769576,21769577,21769578,21769579,21769580,21769581,21769582,21769583],"Crimson Slab":[21714416,21714417,21714418],"Crimson Stairs":[21763896,21763897,21763898,21763899,21763900,21763901,21763902,21763903],"Crimson Stem":[21719642,21719643,21719644,21719645,21719646,21719647],"Crimson Trapdoor":[21733344,21733345,21733346,21733347,21733348,21733349,21733350,21733351,21733352,21733353,21733354,21733355,21733356,21733357,21733358,21733359],"Crimson Wall Sign":[21774384,21774385,21774386,21774387],"Crying Obsidian":[21818013],"Curium":[20805897],"Cut Copper Block":[21827208,21827209,21827210,21827211,21827212,21827213,21827214,21827215],"Cut Copper Slab Slab":[21827976,21827977,21827978,21827979,21827980,21827981,21827982,21827983,21827984,21827985,21827986,21827987,21827988,21827989,21827990,21827991,21827992,21827993,21827994,21827995,21827996,21827997,21827998,21827999],"Cut Copper Stairs":[21830080,21830081,21830082,21830083,21830084,21830085,21830086,21830087,21830088,21830089,21830090,21830091,21830092,21830093,21830094,21830095,21830096,21830097,21830098,21830099,21830100,21830101,21830102,21830103,21830104,21830105,21830106,21830107,21830108,21830109,21830110,21830111,21830112,21830113,21830114,21830115,21830116,21830117,21830118,21830119,21830120,21830121,21830122,21830123,21830124,21830125,21830126,21830127,21830128,21830129,21830130,21830131,21830132,21830133,21830134,21830135,21830136,21830137,21830138,21830139,21830140,21830141,21830142,21830143],"Cut Red Sandstone":[20674620],"Cut Red Sandstone Slab":[20676996,20676997,20676999],"Cut Sandstone":[20679139],"Cut Sandstone Slab":[20681972,20681973,20681974],"Dandelion":[20686168],"Dark Oak Button":[20688624,20688625,20688626,20688627,20688630,20688631,20688632,20688633,20688634,20688635,20688638,20688639],"Dark Oak Door":[20690144,20690145,20690146,20690147,20690148,20690149,20690150,20690151,20690152,20690153,20690154,20690155,20690156,20690157,20690158,20690159,20690160,20690161,20690162,20690163,20690164,20690165,20690166,20690167,20690168,20690169,20690170,20690171,20690172,20690173,20690174,20690175],"Dark Oak Fence":[20692929],"Dark Oak Fence Gate":[20694464,20694465,20694466,20694467,20694468,20694469,20694470,20694471,20694472,20694473,20694474,20694475,20694476,20694477,20694478,20694479],"Dark Oak Leaves":[20696240,20696241,20696242,20696243],"Dark Oak Log":[20698832,20698833,20698836,20698837,20698838,20698839],"Dark Oak Planks":[20699858],"Dark Oak Pressure Plate":[20701726,20701727],"Dark Oak Sapling":[20704518,20704519],"Dark Oak Sign":[20705760,20705761,20705762,20705763,20705764,20705765,20705766,20705767,20705768,20705769,20705770,20705771,20705772,20705773,20705774,20705775],"Dark Oak Slab":[20708816,20708817,20708818],"Dark Oak Stairs":[20711344,20711345,20711346,20711347,20711348,20711349,20711350,20711351],"Dark Oak Trapdoor":[20713024,20713025,20713026,20713027,20713028,20713029,20713030,20713031,20713032,20713033,20713034,20713035,20713036,20713037,20713038,20713039],"Dark Oak Wall Sign":[20715224,20715225,20715226,20715227],"Dark Oak Wood":[20716738,20716739,20716740,20716741,20716742,20716743],"Dark Prismarine":[20719220],"Dark Prismarine Slab":[20721200,20721202,20721203],"Dark Prismarine Stairs":[20722592,20722593,20722594,20722595,20722596,20722597,20722598,20722599],"Darmstadtium":[20809292],"Daylight Sensor":[20724416,20724417,20724418,20724419,20724420,20724421,20724422,20724423,20724424,20724425,20724426,20724427,20724428,20724429,20724430,20724431,20724432,20724433,20724434,20724435,20724436,20724437,20724438,20724439,20724440,20724441,20724442,20724443,20724444,20724445,20724446,20724447],"Dead Bush":[20727625],"Deepslate":[21640384,21640385,21640387],"Deepslate Brick Slab":[21644892,21644893,21644894],"Deepslate Brick Stairs":[21646904,21646905,21646906,21646907,21646908,21646909,21646910,21646911],"Deepslate Brick Wall":[21649152,21649153,21649154,21649155,21649156,21649157,21649158,21649159,21649160,21649161,21649162,21649163,21649164,21649165,21649166,21649167,21649168,21649169,21649170,21649171,21649172,21649173,21649174,21649175,21649176,21649177,21649178,21649179,21649180,21649181,21649182,21649183,21649184,21649185,21649186,21649187,21649188,21649189,21649190,21649191,21649192,21649193,21649194,21649195,21649196,21649197,21649198,21649199,21649200,21649201,21649202,21649203,21649204,21649205,21649206,21649207,21649208,21649209,21649210,21649211,21649212,21649213,21649214,21649215,21649217,21649232,21649233,21649234,21649235,21649236,21649237,21649238,21649239,21649240,21649241,21649242,21649243,21649244,21649245,21649246,21649247,21649280,21649281,21649282,21649283,21649284,21649285,21649286,21649287,21649288,21649289,21649290,21649291,21649292,21649293,21649294,21649295,21649296,21649297,21649298,21649299,21649300,21649301,21649302,21649303,21649304,21649305,21649306,21649307,21649308,21649309,21649310,21649311,21649312,21649313,21649314,21649315,21649316,21649317,21649318,21649319,21649320,21649321,21649322,21649323,21649324,21649325,21649326,21649327,21649328,21649329,21649330,21649331,21649332,21649333,21649334,21649335,21649336,21649337,21649338,21649339,21649340,21649341,21649342,21649343,21649345,21649360,21649361,21649362,21649363,21649364,21649365,21649366,21649367,21649368,21649369,21649370,21649371,21649372,21649373,21649374,21649375],"Deepslate Bricks":[21642473],"Deepslate Coal Ore":[21783821],"Deepslate Copper Ore":[21797493],"Deepslate Diamond Ore":[21785055],"Deepslate Emerald Ore":[21788073],"Deepslate Gold Ore":[21795656],"Deepslate Iron Ore":[21794765],"Deepslate Lapis Lazuli Ore":[21788910],"Deepslate Redstone Ore":[21791572,21791573],"Deepslate Tile Slab":[21655484,21655485,21655487],"Deepslate Tile Stairs":[21657328,21657329,21657330,21657331,21657332,21657333,21657334,21657335],"Deepslate Tile Wall":[21658912,21658913,21658914,21658915,21658916,21658917,21658918,21658919,21658920,21658921,21658922,21658923,21658924,21658925,21658926,21658927,21658930,21658944,21658945,21658946,21658947,21658948,21658949,21658950,21658951,21658952,21658953,21658954,21658955,21658956,21658957,21658958,21658959,21658960,21658961,21658962,21658963,21658964,21658965,21658966,21658967,21658968,21658969,21658970,21658971,21658972,21658973,21658974,21658975,21658976,21658977,21658978,21658979,21658980,21658981,21658982,21658983,21658984,21658985,21658986,21658987,21658988,21658989,21658990,21658991,21658992,21658993,21658994,21658995,21658996,21658997,21658998,21658999,21659000,21659001,21659002,21659003,21659004,21659005,21659006,21659007,21659040,21659041,21659042,21659043,21659044,21659045,21659046,21659047,21659048,21659049,21659050,21659051,21659052,21659053,21659054,21659055,21659058,21659072,21659073,21659074,21659075,21659076,21659077,21659078,21659079,21659080,21659081,21659082,21659083,21659084,21659085,21659086,21659087,21659088,21659089,21659090,21659091,21659092,21659093,21659094,21659095,21659096,21659097,21659098,21659099,21659100,21659101,21659102,21659103,21659104,21659105,21659106,21659107,21659108,21659109,21659110,21659111,21659112,21659113,21659114,21659115,21659116,21659117,21659118,21659119,21659120,21659121,21659122,21659123,21659124,21659125,21659126,21659127,21659128,21659129,21659130,21659131,21659132,21659133,21659134,21659135],"Deepslate Tiles":[21652461],"Detector Rail":[20728240,20728241,20728242,20728243,20728246,20728247,20728248,20728249,20728250,20728251,20728254,20728255],"Diamond Block":[20730418],"Diamond Ore":[20733642],"Diorite":[20734079],"Diorite Slab":[20737469,20737470,20737471],"Diorite Stairs":[20739504,20739505,20739506,20739507,20739508,20739509,20739510,20739511],"Diorite Wall":[20741920,20741921,20741922,20741923,20741924,20741925,20741926,20741927,20741928,20741929,20741930,20741931,20741932,20741933,20741934,20741935,20741937,20741952,20741953,20741954,20741955,20741956,20741957,20741958,20741959,20741960,20741961,20741962,20741963,20741964,20741965,20741966,20741967,20741968,20741969,20741970,20741971,20741972,20741973,20741974,20741975,20741976,20741977,20741978,20741979,20741980,20741981,20741982,20741983,20741984,20741985,20741986,20741987,20741988,20741989,20741990,20741991,20741992,20741993,20741994,20741995,20741996,20741997,20741998,20741999,20742000,20742001,20742002,20742003,20742004,20742005,20742006,20742007,20742008,20742009,20742010,20742011,20742012,20742013,20742014,20742015,20742048,20742049,20742050,20742051,20742052,20742053,20742054,20742055,20742056,20742057,20742058,20742059,20742060,20742061,20742062,20742063,20742065,20742080,20742081,20742082,20742083,20742084,20742085,20742086,20742087,20742088,20742089,20742090,20742091,20742092,20742093,20742094,20742095,20742096,20742097,20742098,20742099,20742100,20742101,20742102,20742103,20742104,20742105,20742106,20742107,20742108,20742109,20742110,20742111,20742112,20742113,20742114,20742115,20742116,20742117,20742118,20742119,20742120,20742121,20742122,20742123,20742124,20742125,20742126,20742127,20742128,20742129,20742130,20742131,20742132,20742133,20742134,20742135,20742136,20742137,20742138,20742139,20742140,20742141,20742142,20742143],"Dirt":[20742760,20742761,20742763],"Double Pitcher Crop":[21950064,21950065,21950066,21950067],"Double Tallgrass":[20744902,20744903],"Dragon Egg":[20747804],"Dried Kelp Block":[20749160],"Dubnium":[20811286],"Dyed Candle":[21834240,21834241,21834242,21834243,21834244,21834245,21834246,21834247,21834248,21834249,21834250,21834251,21834252,21834253,21834254,21834255,21834256,21834257,21834258,21834259,21834260,21834261,21834262,21834263,21834264,21834265,21834266,21834267,21834268,21834269,21834270,21834271,21834272,21834273,21834274,21834275,21834276,21834277,21834278,21834279,21834280,21834281,21834282,21834283,21834284,21834285,21834286,21834287,21834288,21834289,21834290,21834291,21834292,21834293,21834294,21834295,21834296,21834297,21834298,21834299,21834300,21834301,21834302,21834303,21834304,21834305,21834306,21834307,21834308,21834309,21834310,21834311,21834312,21834313,21834314,21834315,21834316,21834317,21834318,21834319,21834320,21834321,21834322,21834323,21834324,21834325,21834326,21834327,21834328,21834329,21834330,21834331,21834332,21834333,21834334,21834335,21834336,21834337,21834338,21834339,21834340,21834341,21834342,21834343,21834344,21834345,21834346,21834347,21834348,21834349,21834350,21834351,21834352,21834353,21834354,21834355,21834356,21834357,21834358,21834359,21834360,21834361,21834362,21834363,21834364,21834365,21834366,21834367],"Dyed Shulker Box":[20750816,20750817,20750818,20750819,20750820,20750821,20750822,20750823,20750824,20750825,20750826,20750827,20750828,20750829,20750830,20750831],"Dysprosium":[20812845],"Einsteinium":[20813977],"Element Constructor":[20799584,20799585,20799586,20799587],"Emerald Block":[20998956],"Emerald Ore":[21001840],"Enchanting Table":[21002728],"End Portal Frame":[21005904,21005905,21005906,21005907,21005908,21005909,21005910,21005911],"End Rod":[21006744,21006745,21006746,21006747,21006750,21006751],"End Stone":[21010195],"End Stone Brick Slab":[21011608,21011609,21011611],"End Stone Brick Stairs":[21014200,21014201,21014202,21014203,21014204,21014205,21014206,21014207],"End Stone Brick Wall":[21015589,21015600,21015601,21015602,21015603,21015604,21015605,21015606,21015607,21015608,21015609,21015610,21015611,21015612,21015613,21015614,21015615,21015616,21015617,21015618,21015619,21015620,21015621,21015622,21015623,21015624,21015625,21015626,21015627,21015628,21015629,21015630,21015631,21015632,21015633,21015634,21015635,21015636,21015637,21015638,21015639,21015640,21015641,21015642,21015643,21015644,21015645,21015646,21015647,21015648,21015649,21015650,21015651,21015652,21015653,21015654,21015655,21015656,21015657,21015658,21015659,21015660,21015661,21015662,21015663,21015664,21015665,21015666,21015667,21015668,21015669,21015670,21015671,21015672,21015673,21015674,21015675,21015676,21015677,21015678,21015679,21015717,21015728,21015729,21015730,21015731,21015732,21015733,21015734,21015735,21015736,21015737,21015738,21015739,21015740,21015741,21015742,21015743,21015744,21015745,21015746,21015747,21015748,21015749,21015750,21015751,21015752,21015753,21015754,21015755,21015756,21015757,21015758,21015759,21015760,21015761,21015762,21015763,21015764,21015765,21015766,21015767,21015768,21015769,21015770,21015771,21015772,21015773,21015774,21015775,21015776,21015777,21015778,21015779,21015780,21015781,21015782,21015783,21015784,21015785,21015786,21015787,21015788,21015789,21015790,21015791,21015792,21015793,21015794,21015795,21015796,21015797,21015798,21015799,21015800,21015801,21015802,21015803,21015804,21015805,21015806,21015807],"End Stone Bricks":[21017562],"Ender Chest":[21019660,21019661,21019662,21019663],"Erbium":[20817537],"Europium":[20819484],"Fake Wooden Slab":[21021009,21021010,21021011],"Farmland":[21022720,21022721,21022722,21022723,21022724,21022725,21022726,21022727,21022728,21022729,21022730,21022731,21022732,21022733,21022734,21022735,21022736,21022737,21022738,21022739,21022740,21022741,21022742,21022743,21022744,21022745,21022746,21022747,21022748,21022749,21022750,21022751,21022752,21022753,21022754,21022755,21022756,21022757,21022758,21022759,21022760,21022761,21022762,21022763,21022764,21022765,21022766,21022767,21022768,21022769,21022770,21022771,21022772,21022773,21022774,21022775,21022776,21022777,21022778,21022779,21022780,21022781,21022782,21022783,21022784,21022785,21022786,21022787,21022788,21022789,21022790,21022791,21022792,21022793,21022794,21022795,21022796,21022797,21022798,21022799,21022800,21022801,21022802,21022803,21022804,21022805,21022806,21022807,21022808,21022809,21022810,21022811,21022812,21022813,21022814,21022815,21022816,21022817,21022818,21022819,21022820,21022821,21022822,21022823,21022824,21022825,21022826,21022827,21022828,21022829,21022830,21022831,21022832,21022833,21022834,21022835,21022836,21022837,21022838,21022839,21022840,21022841,21022842,21022843,21022844,21022845,21022846,21022847,21022848,21022849,21022850,21022851,21022852,21022853,21022854,21022855,21022856,21022857,21022858,21022859,21022860,21022861,21022862,21022863,21022864,21022865,21022866,21022867,21022868,21022869,21022870,21022871,21022872,21022873,21022874,21022875,21022876,21022877,21022878,21022879,21022880,21022881,21022882,21022883,21022884,21022885,21022886,21022887,21022888,21022889,21022890,21022891,21022892,21022893,21022894,21022895,21022896,21022897,21022898,21022899,21022900,21022901,21022902,21022903,21022904,21022905,21022906,21022907,21022908,21022909,21022910,21022911,21022912,21022913,21022914,21022915,21022916,21022917,21022918,21022919,21022920,21022921,21022922,21022923,21022924,21022925,21022926,21022927,21022928,21022929,21022930,21022931,21022932,21022933,21022934,21022935,21022936,21022937,21022938,21022939,21022940,21022941,21022942,21022943,21022944,21022945,21022946,21022947,21022948,21022949,21022950,21022951,21022952,21022953,21022954,21022955,21022956,21022957,21022958,21022959,21022960,21022961,21022962,21022963,21022964,21022965,21022966,21022967,21022968,21022969,21022970,21022971,21022972,21022973,21022974,21022975,21022976,21022977,21022978,21022979,21022980,21022981,21022982,21022983,21022984,21022985,21022986,21022987,21022988,21022989,21022990,21022991,21022992,21022993,21022994,21022995,21022996,21022997,21022998,21022999,21023000,21023001,21023002,21023003,21023004,21023005,21023006,21023007,21023008,21023009,21023010,21023011,21023012,21023013,21023014,21023015,21023016,21023017,21023018,21023019,21023020,21023021,21023022,21023023,21023024,21023025,21023026,21023027,21023028,21023029,21023030,21023031,21023032,21023033,21023034,21023035,21023036,21023037,21023038,21023039,21023040,21023041,21023042,21023043,21023044,21023045,21023046,21023047,21023048,21023049,21023050,21023051,21023052,21023053,21023054,21023055,21023056,21023057,21023058,21023059,21023060,21023061,21023062,21023063,21023064,21023065,21023066,21023067,21023068,21023069,21023070,21023071,21023072,21023073,21023074,21023075,21023076,21023077,21023078,21023079,21023080,21023081,21023082,21023083,21023084,21023085,21023086,21023087,21023088,21023089,21023090,21023091,21023092,21023093,21023094,21023095,21023096,21023097,21023098,21023099,21023100,21023101,21023102,21023103,21023104,21023105,21023106,21023107,21023108,21023109,21023110,21023111,21023112,21023113,21023114,21023115,21023116,21023117,21023118,21023119,21023120,21023121,21023122,21023123,21023124,21023125,21023126,21023127,21023128,21023129,21023130,21023131,21023132,21023133,21023134,21023135,21023136,21023137,21023138,21023139,21023140,21023141,21023142,21023143,21023144,21023145,21023146,21023147,21023148,21023149,21023150,21023151,21023152,21023153,21023154,21023155,21023156,21023157,21023158,21023159,21023160,21023161,21023162,21023163,21023164,21023165,21023166,21023167,21023168,21023169,21023170,21023171,21023172,21023173,21023174,21023175,21023176,21023177,21023178,21023179,21023180,21023181,21023182,21023183,21023184,21023185,21023186,21023187,21023188,21023189,21023190,21023191,21023192,21023193,21023194,21023195,21023196,21023197,21023198,21023199,21023200,21023201,21023202,21023203,21023204,21023205,21023206,21023207,21023208,21023209,21023210,21023211,21023212,21023213,21023214,21023215,21023216,21023217,21023218,21023219,21023220,21023221,21023222,21023223,21023224,21023225,21023226,21023227,21023228,21023229,21023230,21023231,21023232,21023233,21023234,21023235,21023236,21023237,21023238,21023239,21023240,21023241,21023242,21023243,21023244,21023245,21023246,21023247,21023248,21023249,21023250,21023251,21023252,21023253,21023254,21023255,21023256,21023257,21023258,21023259,21023260,21023261,21023262,21023263,21023264,21023265,21023266,21023267,21023268,21023269,21023270,21023271,21023272,21023273,21023274,21023275,21023276,21023277,21023278,21023279,21023280,21023281,21023282,21023283,21023284,21023285,21023286,21023287,21023288,21023289,21023290,21023291,21023292,21023293,21023294,21023295,21023296,21023297,21023298,21023299,21023300,21023301,21023302,21023303,21023304,21023305,21023306,21023307,21023308,21023309,21023310,21023311,21023312,21023313,21023314,21023315,21023316,21023317,21023318,21023319,21023320,21023321,21023322,21023323,21023324,21023325,21023326,21023327,21023328,21023329,21023330,21023331,21023332,21023333,21023334,21023335,21023336,21023337,21023338,21023339,21023340,21023341,21023342,21023343,21023344,21023345,21023346,21023347,21023348,21023349,21023350,21023351,21023352,21023353,21023354,21023355,21023356,21023357,21023358,21023359,21023360,21023361,21023362,21023363,21023364,21023365,21023366,21023367,21023368,21023369,21023370,21023371,21023372,21023373,21023374,21023375,21023376,21023377,21023378,21023379,21023380,21023381,21023382,21023383,21023384,21023385,21023386,21023387,21023388,21023389,21023390,21023391,21023392,21023393,21023394,21023395,21023396,21023397,21023398,21023399,21023400,21023401,21023402,21023403,21023404,21023405,21023406,21023407,21023408,21023409,21023410,21023411,21023412,21023413,21023414,21023415,21023416,21023417,21023418,21023419,21023420,21023421,21023422,21023423,21023424,21023425,21023426,21023427,21023428,21023429,21023430,21023431,21023432,21023433,21023434,21023435,21023436,21023437,21023438,21023439,21023440,21023441,21023442,21023443,21023444,21023445,21023446,21023447,21023448,21023449,21023450,21023451,21023452,21023453,21023454,21023455,21023456,21023457,21023458,21023459,21023460,21023461,21023462,21023463,21023464,21023465,21023466,21023467,21023468,21023469,21023470,21023471,21023472,21023473,21023474,21023475,21023476,21023477,21023478,21023479,21023480,21023481,21023482,21023483,21023484,21023485,21023486,21023487,21023488,21023489,21023490,21023491,21023492,21023493,21023494,21023495,21023496,21023497,21023498,21023499,21023500,21023501,21023502,21023503,21023504,21023505,21023506,21023507,21023508,21023509,21023510,21023511,21023512,21023513,21023514,21023515,21023516,21023517,21023518,21023519,21023520,21023521,21023522,21023523,21023524,21023525,21023526,21023527,21023528,21023529,21023530,21023531,21023532,21023533,21023534,21023535,21023536,21023537,21023538,21023539,21023540,21023541,21023542,21023543,21023544,21023545,21023546,21023547,21023548,21023549,21023550,21023551,21023552,21023553,21023554,21023555,21023556,21023557,21023558,21023559,21023560,21023561,21023562,21023563,21023564,21023565,21023566,21023567,21023568,21023569,21023570,21023571,21023572,21023573,21023574,21023575,21023576,21023577,21023578,21023579,21023580,21023581,21023582,21023583,21023584,21023585,21023586,21023587,21023588,21023589,21023590,21023591,21023592,21023593,21023594,21023595,21023596,21023597,21023598,21023599,21023600,21023601,21023602,21023603,21023604,21023605,21023606,21023607,21023608,21023609,21023610,21023611,21023612,21023613,21023614,21023615,21023616,21023617,21023618,21023619,21023620,21023621,21023622,21023623,21023624,21023625,21023626,21023627,21023628,21023629,21023630,21023631,21023632,21023633,21023634,21023635,21023636,21023637,21023638,21023639,21023640,21023641,21023642,21023643,21023644,21023645,21023646,21023647,21023648,21023649,21023650,21023651,21023652,21023653,21023654,21023655,21023656,21023657,21023658,21023659,21023660,21023661,21023662,21023663,21023664,21023665,21023666,21023667,21023668,21023669,21023670,21023671,21023672,21023673,21023674,21023675,21023676,21023677,21023678,21023679,21023680,21023681,21023682,21023683,21023684,21023685,21023686,21023687,21023688,21023689,21023690,21023691,21023692,21023693,21023694,21023695,21023696,21023697,21023698,21023699,21023700,21023701,21023702,21023703,21023704,21023705,21023706,21023707,21023708,21023709,21023710,21023711,21023712,21023713,21023714,21023715,21023716,21023717,21023718,21023719,21023720,21023721,21023722,21023723,21023724,21023725,21023726,21023727,21023728,21023729,21023730,21023731,21023732,21023733,21023734,21023735,21023736,21023737,21023738,21023739,21023740,21023741,21023742,21023743,21024256,21024257,21024258,21024259,21024260,21024261,21024262,21024263,21024264,21024265,21024266,21024267,21024268,21024269,21024270,21024271,21024272,21024273,21024274,21024275,21024276,21024277,21024278,21024279,21024280,21024281,21024282,21024283,21024284,21024285,21024286,21024287,21024288,21024289,21024290,21024291,21024292,21024293,21024294,21024295,21024296,21024297,21024298,21024299,21024300,21024301,21024302,21024303,21024304,21024305,21024306,21024307,21024308,21024309,21024310,21024311,21024312,21024313,21024314,21024315,21024316,21024317,21024318,21024319,21024320,21024321,21024322,21024323,21024324,21024325,21024326,21024327,21024328,21024329,21024330,21024331,21024332,21024333,21024334,21024335,21024336,21024337,21024338,21024339,21024340,21024341,21024342,21024343,21024344,21024345,21024346,21024347,21024348,21024349,21024350,21024351,21024352,21024353,21024354,21024355,21024356,21024357,21024358,21024359,21024360,21024361,21024362,21024363,21024364,21024365,21024366,21024367,21024368,21024369,21024370,21024371,21024372,21024373,21024374,21024375,21024376,21024377,21024378,21024379,21024380,21024381,21024382,21024383,21024384,21024385,21024386,21024387,21024388,21024389,21024390,21024391,21024392,21024393,21024394,21024395,21024396,21024397,21024398,21024399,21024400,21024401,21024402,21024403,21024404,21024405,21024406,21024407,21024408,21024409,21024410,21024411,21024412,21024413,21024414,21024415,21024416,21024417,21024418,21024419,21024420,21024421,21024422,21024423,21024424,21024425,21024426,21024427,21024428,21024429,21024430,21024431,21024432,21024433,21024434,21024435,21024436,21024437,21024438,21024439,21024440,21024441,21024442,21024443,21024444,21024445,21024446,21024447,21024448,21024449,21024450,21024451,21024452,21024453,21024454,21024455,21024456,21024457,21024458,21024459,21024460,21024461,21024462,21024463,21024464,21024465,21024466,21024467,21024468,21024469,21024470,21024471,21024472,21024473,21024474,21024475,21024476,21024477,21024478,21024479,21024480,21024481,21024482,21024483,21024484,21024485,21024486,21024487,21024488,21024489,21024490,21024491,21024492,21024493,21024494,21024495,21024496,21024497,21024498,21024499,21024500,21024501,21024502,21024503,21024504,21024505,21024506,21024507,21024508,21024509,21024510,21024511,21024640,21024641,21024642,21024643,21024644,21024645,21024646,21024647,21024648,21024649,21024650,21024651,21024652,21024653,21024654,21024655,21024664,21024665,21024666,21024667,21024668,21024669,21024670,21024671],"Fermium":[20821256],"Fern":[21026077],"Fire Block":[21027184,21027185,21027186,21027187,21027188,21027189,21027190,21027191,21027192,21027193,21027194,21027195,21027196,21027197,21027198,21027199],"Flerovium":[20823370],"Fletching Table":[21030063],"Flower Pot":[21031217],"Flowering Azalea Leaves":[21888168,21888169,21888170,21888171],"Fluorine":[20824372],"Francium":[20827305],"Froglight":[21872001,21872002,21872003,21872005,21872006,21872007,21872009,21872010,21872011],"Frosted Ice":[21034052,21034053,21034054,21034055],"Furnace":[21036512,21036513,21036514,21036515,21036516,21036517,21036518,21036519],"Gadolinium":[20828273],"Gallium":[20832169],"Germanium":[20833509],"Gilded Blackstone":[21820646],"Glass":[21038329],"Glass Pane":[21040585],"Glazed Terracotta":[21585152,21585153,21585154,21585155,21585156,21585157,21585158,21585159,21585160,21585161,21585162,21585163,21585164,21585165,21585166,21585167,21585168,21585169,21585170,21585171,21585172,21585173,21585174,21585175,21585176,21585177,21585178,21585179,21585180,21585181,21585182,21585183,21585184,21585185,21585186,21585187,21585188,21585189,21585190,21585191,21585192,21585193,21585194,21585195,21585196,21585197,21585198,21585199,21585200,21585201,21585202,21585203,21585204,21585205,21585206,21585207,21585208,21585209,21585210,21585211,21585212,21585213,21585214,21585215],"Glow Item Frame":[21881552,21881553,21881556,21881557,21881558,21881559,21881560,21881561,21881564,21881565,21881566,21881567],"Glow Lichen":[21893760,21893761,21893762,21893763,21893764,21893765,21893766,21893767,21893768,21893769,21893770,21893771,21893772,21893773,21893774,21893775,21893776,21893777,21893778,21893779,21893780,21893781,21893782,21893783,21893784,21893785,21893786,21893787,21893788,21893789,21893790,21893791,21893792,21893793,21893794,21893795,21893796,21893797,21893798,21893799,21893800,21893801,21893802,21893803,21893804,21893805,21893806,21893807,21893808,21893809,21893810,21893811,21893812,21893813,21893814,21893815,21893816,21893817,21893818,21893819,21893820,21893821,21893822,21893823],"Glowing Obsidian":[21043028],"Glowstone":[21043624],"Gold":[20835704],"Gold Block":[21046696],"Gold Ore":[21049010],"Granite":[21049750],"Granite Slab":[21053256,21053257,21053258],"Granite Stairs":[21054536,21054537,21054538,21054539,21054540,21054541,21054542,21054543],"Granite Wall":[21055488,21055489,21055490,21055491,21055492,21055493,21055494,21055495,21055496,21055497,21055498,21055499,21055500,21055501,21055502,21055503,21055504,21055505,21055506,21055507,21055508,21055509,21055510,21055511,21055512,21055513,21055514,21055515,21055516,21055517,21055518,21055519,21055520,21055521,21055522,21055523,21055524,21055525,21055526,21055527,21055528,21055529,21055530,21055531,21055532,21055533,21055534,21055535,21055536,21055537,21055538,21055539,21055540,21055541,21055542,21055543,21055544,21055545,21055546,21055547,21055548,21055549,21055550,21055551,21055584,21055585,21055586,21055587,21055588,21055589,21055590,21055591,21055592,21055593,21055594,21055595,21055596,21055597,21055598,21055599,21055615,21055616,21055617,21055618,21055619,21055620,21055621,21055622,21055623,21055624,21055625,21055626,21055627,21055628,21055629,21055630,21055631,21055632,21055633,21055634,21055635,21055636,21055637,21055638,21055639,21055640,21055641,21055642,21055643,21055644,21055645,21055646,21055647,21055648,21055649,21055650,21055651,21055652,21055653,21055654,21055655,21055656,21055657,21055658,21055659,21055660,21055661,21055662,21055663,21055664,21055665,21055666,21055667,21055668,21055669,21055670,21055671,21055672,21055673,21055674,21055675,21055676,21055677,21055678,21055679,21055712,21055713,21055714,21055715,21055716,21055717,21055718,21055719,21055720,21055721,21055722,21055723,21055724,21055725,21055726,21055727,21055743],"Grass":[21058118],"Grass Path":[21061327],"Gravel":[21062456],"Green Torch":[21068985,21068986,21068987,21068988,21068989],"Hafnium":[20837560],"Hanging Roots":[21842426],"Hardened Clay":[21071190],"Hardened Glass":[21072075],"Hardened Glass Pane":[21075766],"Hassium":[20839994],"Hay Bale":[21077748,21077749,21077750],"Heat Block":[20625734],"Helium":[20840704],"Holmium":[20842739],"Honeycomb Block":[21782395],"Hopper":[21079520,21079521,21079525,21079526,21079527,21079528,21079529,21079533,21079534,21079535],"Hydrogen":[20844910],"Ice":[21080304],"Indium":[20848311],"Infested Chiseled Stone Brick":[21083943],"Infested Cobblestone":[21085393],"Infested Cracked Stone Brick":[21087627],"Infested Mossy Stone Brick":[21088835],"Infested Stone":[21091396],"Infested Stone Brick":[21092358],"Invisible Bedrock":[21100447],"Iodine":[20850310],"Iridium":[20851638],"Iron":[20854572],"Iron Bars":[21104146],"Iron Block":[21101750],"Iron Door":[21105792,21105793,21105794,21105795,21105796,21105797,21105798,21105799,21105800,21105801,21105802,21105803,21105804,21105805,21105806,21105807,21105808,21105809,21105810,21105811,21105812,21105813,21105814,21105815,21105816,21105817,21105818,21105819,21105820,21105821,21105822,21105823],"Iron Ore":[21107313],"Iron Trapdoor":[21109264,21109265,21109266,21109267,21109268,21109269,21109270,21109271,21109272,21109273,21109274,21109275,21109276,21109277,21109278,21109279],"Item Frame":[21111952,21111953,21111954,21111955,21111956,21111957,21111960,21111961,21111962,21111963,21111964,21111965],"Jack o'Lantern":[21179952,21179953,21179954,21179955],"Jukebox":[21113057],"Jungle Button":[21115488,21115489,21115490,21115491,21115492,21115493,21115496,21115497,21115498,21115499,21115500,21115501],"Jungle Door":[21117568,21117569,21117570,21117571,21117572,21117573,21117574,21117575,21117576,21117577,21117578,21117579,21117580,21117581,21117582,21117583,21117584,21117585,21117586,21117587,21117588,21117589,21117590,21117591,21117592,21117593,21117594,21117595,21117596,21117597,21117598,21117599],"Jungle Fence":[21119582],"Jungle Fence Gate":[21122144,21122145,21122146,21122147,21122148,21122149,21122150,21122151,21122152,21122153,21122154,21122155,21122156,21122157,21122158,21122159],"Jungle Leaves":[21124916,21124917,21124918,21124919],"Jungle Log":[21126656,21126657,21126658,21126659,21126662,21126663],"Jungle Planks":[21127877],"Jungle Pressure Plate":[21130628,21130629],"Jungle Sapling":[21132062,21132063],"Jungle Sign":[21134400,21134401,21134402,21134403,21134404,21134405,21134406,21134407,21134408,21134409,21134410,21134411,21134412,21134413,21134414,21134415],"Jungle Slab":[21135720,21135721,21135722],"Jungle Stairs":[21137656,21137657,21137658,21137659,21137660,21137661,21137662,21137663],"Jungle Trapdoor":[21139728,21139729,21139730,21139731,21139732,21139733,21139734,21139735,21139736,21139737,21139738,21139739,21139740,21139741,21139742,21139743],"Jungle Wall Sign":[21143348,21143349,21143350,21143351],"Jungle Wood":[21145480,21145481,21145484,21145485,21145486,21145487],"Krypton":[20856167],"Lab Table":[21146608,21146609,21146610,21146611],"Ladder":[21148984,21148985,21148986,21148987],"Lantern":[21149740,21149741],"Lanthanum":[20858521],"Lapis Lazuli Block":[21152040],"Lapis Lazuli Ore":[21154437],"Large Fern":[21156644,21156645],"Lava":[21159648,21159649,21159650,21159651,21159652,21159653,21159654,21159655,21159656,21159657,21159658,21159659,21159660,21159661,21159662,21159663,21159664,21159665,21159666,21159667,21159668,21159669,21159670,21159671,21159672,21159673,21159674,21159675,21159676,21159677,21159678,21159679],"Lava Cauldron":[21857912,21857913,21857914,21857915,21857916,21857917],"Lawrencium":[20860323],"Lead":[20861284],"Lectern":[21160360,21160361,21160362,21160363,21160364,21160365,21160366,21160367],"Legacy Stonecutter":[21162264],"Lever":[21165776,21165777,21165778,21165779,21165780,21165781,21165782,21165783,21165784,21165785,21165786,21165787,21165788,21165789,21165790,21165791],"Light Block":[21629408,21629409,21629410,21629411,21629412,21629413,21629414,21629415,21629416,21629417,21629418,21629419,21629420,21629421,21629422,21629423],"Lightning Rod":[21821570,21821571,21821572,21821573,21821574,21821575],"Lilac":[21171834,21171835],"Lily Pad":[21174431],"Lily of the Valley":[21173216],"Lithium":[20863911],"Livermorium":[20865878],"Loom":[21182044,21182045,21182046,21182047],"Lutetium":[20867537],"Magma Block":[21186488],"Magnesium":[20869155],"Manganese":[20872294],"Mangrove Button":[21735552,21735553,21735554,21735555,21735558,21735559,21735560,21735561,21735562,21735563,21735566,21735567],"Mangrove Door":[21749248,21749249,21749250,21749251,21749252,21749253,21749254,21749255,21749256,21749257,21749258,21749259,21749260,21749261,21749262,21749263,21749264,21749265,21749266,21749267,21749268,21749269,21749270,21749271,21749272,21749273,21749274,21749275,21749276,21749277,21749278,21749279],"Mangrove Fence":[21706659],"Mangrove Fence Gate":[21755424,21755425,21755426,21755427,21755428,21755429,21755430,21755431,21755432,21755433,21755434,21755435,21755436,21755437,21755438,21755439],"Mangrove Leaves":[21884612,21884613,21884614,21884615],"Mangrove Log":[21717272,21717273,21717274,21717275,21717278,21717279],"Mangrove Planks":[21700363],"Mangrove Pressure Plate":[21742138,21742139],"Mangrove Roots":[21868046],"Mangrove Sign":[21766688,21766689,21766690,21766691,21766692,21766693,21766694,21766695,21766696,21766697,21766698,21766699,21766700,21766701,21766702,21766703],"Mangrove Slab":[21711233,21711234,21711235],"Mangrove Stairs":[21760624,21760625,21760626,21760627,21760628,21760629,21760630,21760631],"Mangrove Trapdoor":[21729584,21729585,21729586,21729587,21729588,21729589,21729590,21729591,21729592,21729593,21729594,21729595,21729596,21729597,21729598,21729599],"Mangrove Wall Sign":[21774240,21774241,21774242,21774243],"Mangrove Wood":[21723914,21723915,21723916,21723917,21723918,21723919],"Material Reducer":[21187744,21187745,21187746,21187747],"Meitnerium":[20874589],"Melon Block":[21189795],"Melon Stem":[21190912,21190913,21190914,21190915,21190916,21190917,21190918,21190919,21190920,21190921,21190922,21190923,21190924,21190925,21190926,21190927,21190944,21190945,21190946,21190947,21190948,21190949,21190950,21190951,21190960,21190961,21190962,21190963,21190964,21190965,21190966,21190967,21190968,21190969,21190970,21190971,21190972,21190973,21190974,21190975],"Mendelevium":[20876625],"Mercury":[20877937],"Mob Head":[21193344,21193345,21193346,21193347,21193349,21193350,21193351,21193352,21193353,21193354,21193355,21193357,21193358,21193359,21193376,21193377,21193378,21193379,21193381,21193382,21193383,21193392,21193393,21193394,21193395,21193397,21193398,21193399,21193400,21193401,21193402,21193403,21193405,21193406,21193407],"Molybdenum":[20879537],"Monster Spawner":[21195322],"Moscovium":[20883402],"Mossy Cobblestone":[21197453],"Mossy Cobblestone Slab":[21200052,21200054,21200055],"Mossy Cobblestone Stairs":[21202568,21202569,21202570,21202571,21202572,21202573,21202574,21202575],"Mossy Cobblestone Wall":[21204224,21204225,21204226,21204227,21204228,21204229,21204230,21204231,21204232,21204233,21204234,21204235,21204236,21204237,21204238,21204239,21204240,21204241,21204242,21204243,21204244,21204245,21204246,21204247,21204248,21204249,21204250,21204251,21204252,21204253,21204254,21204255,21204256,21204257,21204258,21204259,21204260,21204261,21204262,21204263,21204264,21204265,21204266,21204267,21204268,21204269,21204270,21204271,21204272,21204273,21204274,21204275,21204276,21204277,21204278,21204279,21204280,21204281,21204282,21204283,21204284,21204285,21204286,21204287,21204288,21204289,21204290,21204291,21204292,21204293,21204294,21204295,21204296,21204297,21204298,21204299,21204300,21204301,21204302,21204303,21204306,21204352,21204353,21204354,21204355,21204356,21204357,21204358,21204359,21204360,21204361,21204362,21204363,21204364,21204365,21204366,21204367,21204368,21204369,21204370,21204371,21204372,21204373,21204374,21204375,21204376,21204377,21204378,21204379,21204380,21204381,21204382,21204383,21204384,21204385,21204386,21204387,21204388,21204389,21204390,21204391,21204392,21204393,21204394,21204395,21204396,21204397,21204398,21204399,21204400,21204401,21204402,21204403,21204404,21204405,21204406,21204407,21204408,21204409,21204410,21204411,21204412,21204413,21204414,21204415,21204416,21204417,21204418,21204419,21204420,21204421,21204422,21204423,21204424,21204425,21204426,21204427,21204428,21204429,21204430,21204431,21204434],"Mossy Stone Brick Slab":[21205992,21205993,21205995],"Mossy Stone Brick Stairs":[21207584,21207585,21207586,21207587,21207588,21207589,21207590,21207591],"Mossy Stone Brick Wall":[21210147,21210160,21210161,21210162,21210163,21210164,21210165,21210166,21210167,21210168,21210169,21210170,21210171,21210172,21210173,21210174,21210175,21210176,21210177,21210178,21210179,21210180,21210181,21210182,21210183,21210184,21210185,21210186,21210187,21210188,21210189,21210190,21210191,21210192,21210193,21210194,21210195,21210196,21210197,21210198,21210199,21210200,21210201,21210202,21210203,21210204,21210205,21210206,21210207,21210208,21210209,21210210,21210211,21210212,21210213,21210214,21210215,21210216,21210217,21210218,21210219,21210220,21210221,21210222,21210223,21210224,21210225,21210226,21210227,21210228,21210229,21210230,21210231,21210232,21210233,21210234,21210235,21210236,21210237,21210238,21210239,21210275,21210288,21210289,21210290,21210291,21210292,21210293,21210294,21210295,21210296,21210297,21210298,21210299,21210300,21210301,21210302,21210303,21210304,21210305,21210306,21210307,21210308,21210309,21210310,21210311,21210312,21210313,21210314,21210315,21210316,21210317,21210318,21210319,21210320,21210321,21210322,21210323,21210324,21210325,21210326,21210327,21210328,21210329,21210330,21210331,21210332,21210333,21210334,21210335,21210336,21210337,21210338,21210339,21210340,21210341,21210342,21210343,21210344,21210345,21210346,21210347,21210348,21210349,21210350,21210351,21210352,21210353,21210354,21210355,21210356,21210357,21210358,21210359,21210360,21210361,21210362,21210363,21210364,21210365,21210366,21210367],"Mossy Stone Bricks":[21212641],"Mud":[21804596],"Mud Brick Slab":[21807848,21807850,21807851],"Mud Brick Stairs":[21810184,21810185,21810186,21810187,21810188,21810189,21810190,21810191],"Mud Brick Wall":[21812238,21812240,21812241,21812242,21812243,21812244,21812245,21812246,21812247,21812248,21812249,21812250,21812251,21812252,21812253,21812254,21812255,21812288,21812289,21812290,21812291,21812292,21812293,21812294,21812295,21812296,21812297,21812298,21812299,21812300,21812301,21812302,21812303,21812304,21812305,21812306,21812307,21812308,21812309,21812310,21812311,21812312,21812313,21812314,21812315,21812316,21812317,21812318,21812319,21812320,21812321,21812322,21812323,21812324,21812325,21812326,21812327,21812328,21812329,21812330,21812331,21812332,21812333,21812334,21812335,21812336,21812337,21812338,21812339,21812340,21812341,21812342,21812343,21812344,21812345,21812346,21812347,21812348,21812349,21812350,21812351,21812366,21812368,21812369,21812370,21812371,21812372,21812373,21812374,21812375,21812376,21812377,21812378,21812379,21812380,21812381,21812382,21812383,21812416,21812417,21812418,21812419,21812420,21812421,21812422,21812423,21812424,21812425,21812426,21812427,21812428,21812429,21812430,21812431,21812432,21812433,21812434,21812435,21812436,21812437,21812438,21812439,21812440,21812441,21812442,21812443,21812444,21812445,21812446,21812447,21812448,21812449,21812450,21812451,21812452,21812453,21812454,21812455,21812456,21812457,21812458,21812459,21812460,21812461,21812462,21812463,21812464,21812465,21812466,21812467,21812468,21812469,21812470,21812471,21812472,21812473,21812474,21812475,21812476,21812477,21812478,21812479],"Mud Bricks":[21805580],"Muddy Mangrove Roots":[21870173,21870174,21870175],"Mushroom Stem":[21214847],"Mycelium":[21215507],"Neodymium":[20883794],"Neon":[20887178],"Neptunium":[20888366],"Nether Brick Fence":[21219143],"Nether Brick Slab":[21219584,21219585,21219587],"Nether Brick Stairs":[21222456,21222457,21222458,21222459,21222460,21222461,21222462,21222463],"Nether Brick Wall":[21223712,21223713,21223714,21223715,21223716,21223717,21223718,21223719,21223720,21223721,21223722,21223723,21223724,21223725,21223726,21223727,21223728,21223744,21223745,21223746,21223747,21223748,21223749,21223750,21223751,21223752,21223753,21223754,21223755,21223756,21223757,21223758,21223759,21223760,21223761,21223762,21223763,21223764,21223765,21223766,21223767,21223768,21223769,21223770,21223771,21223772,21223773,21223774,21223775,21223776,21223777,21223778,21223779,21223780,21223781,21223782,21223783,21223784,21223785,21223786,21223787,21223788,21223789,21223790,21223791,21223792,21223793,21223794,21223795,21223796,21223797,21223798,21223799,21223800,21223801,21223802,21223803,21223804,21223805,21223806,21223807,21223840,21223841,21223842,21223843,21223844,21223845,21223846,21223847,21223848,21223849,21223850,21223851,21223852,21223853,21223854,21223855,21223856,21223872,21223873,21223874,21223875,21223876,21223877,21223878,21223879,21223880,21223881,21223882,21223883,21223884,21223885,21223886,21223887,21223888,21223889,21223890,21223891,21223892,21223893,21223894,21223895,21223896,21223897,21223898,21223899,21223900,21223901,21223902,21223903,21223904,21223905,21223906,21223907,21223908,21223909,21223910,21223911,21223912,21223913,21223914,21223915,21223916,21223917,21223918,21223919,21223920,21223921,21223922,21223923,21223924,21223925,21223926,21223927,21223928,21223929,21223930,21223931,21223932,21223933,21223934,21223935],"Nether Bricks":[21225672],"Nether Gold Ore":[21801107],"Nether Portal":[21229304,21229305],"Nether Quartz Ore":[21230807],"Nether Reactor Core":[21233112],"Nether Wart":[21235588,21235589,21235590,21235591],"Nether Wart Block":[21237079],"Netherite Block":[21848862],"Netherrack":[21239734],"Nickel":[20891060],"Nihonium":[20892811],"Niobium":[20895630],"Nitrogen":[20897160],"Nobelium":[20898460],"Note Block":[21241302],"Oak Button":[21243152,21243153,21243156,21243157,21243158,21243159,21243160,21243161,21243164,21243165,21243166,21243167],"Oak Door":[21245696,21245697,21245698,21245699,21245700,21245701,21245702,21245703,21245704,21245705,21245706,21245707,21245708,21245709,21245710,21245711,21245712,21245713,21245714,21245715,21245716,21245717,21245718,21245719,21245720,21245721,21245722,21245723,21245724,21245725,21245726,21245727],"Oak Fence":[21247506],"Oak Fence Gate":[21248560,21248561,21248562,21248563,21248564,21248565,21248566,21248567,21248568,21248569,21248570,21248571,21248572,21248573,21248574,21248575],"Oak Leaves":[21250248,21250249,21250250,21250251],"Oak Log":[21253352,21253353,21253356,21253357,21253358,21253359],"Oak Planks":[21255504],"Oak Pressure Plate":[21257680,21257681],"Oak Sapling":[21258462,21258463],"Oak Sign":[21262208,21262209,21262210,21262211,21262212,21262213,21262214,21262215,21262216,21262217,21262218,21262219,21262220,21262221,21262222,21262223],"Oak Slab":[21264373,21264374,21264375],"Oak Stairs":[21264984,21264985,21264986,21264987,21264988,21264989,21264990,21264991],"Oak Trapdoor":[21267760,21267761,21267762,21267763,21267764,21267765,21267766,21267767,21267768,21267769,21267770,21267771,21267772,21267773,21267774,21267775],"Oak Wall Sign":[21270344,21270345,21270346,21270347],"Oak Wood":[21272410,21272411,21272412,21272413,21272414,21272415],"Obsidian":[21273247],"Oganesson":[20901372],"Orange Tulip":[21278363],"Osmium":[20902298],"Oxeye Daisy":[21280579],"Oxygen":[20904205],"Packed Ice":[21282488],"Packed Mud":[21814973],"Palladium":[20907027],"Peony":[21283348,21283349],"Phosphorus":[20908773],"Pink Petals":[21933456,21933457,21933458,21933459,21933460,21933461,21933462,21933463,21933464,21933465,21933466,21933467,21933468,21933469,21933470,21933471],"Pink Tulip":[21286997],"Pitcher Crop":[21947997,21947998,21947999],"Pitcher Plant":[21945642,21945643],"Platinum":[20911235],"Plutonium":[20912900],"Podzol":[21290197],"Polished Andesite":[21292531],"Polished Andesite Slab":[21294924,21294925,21294927],"Polished Andesite Stairs":[21295920,21295921,21295922,21295923,21295924,21295925,21295926,21295927],"Polished Basalt":[21592160,21592162,21592163],"Polished Blackstone":[21605632],"Polished Blackstone Brick Slab":[21622088,21622089,21622090],"Polished Blackstone Brick Stairs":[21622992,21622993,21622994,21622995,21622996,21622997,21622998,21622999],"Polished Blackstone Brick Wall":[21624832,21624833,21624834,21624835,21624836,21624837,21624838,21624839,21624840,21624841,21624842,21624843,21624844,21624845,21624846,21624847,21624848,21624849,21624850,21624851,21624852,21624853,21624854,21624855,21624856,21624857,21624858,21624859,21624860,21624861,21624862,21624863,21624864,21624865,21624866,21624867,21624868,21624869,21624870,21624871,21624872,21624873,21624874,21624875,21624876,21624877,21624878,21624879,21624880,21624881,21624882,21624883,21624884,21624885,21624886,21624887,21624888,21624889,21624890,21624891,21624892,21624893,21624894,21624895,21624896,21624912,21624913,21624914,21624915,21624916,21624917,21624918,21624919,21624920,21624921,21624922,21624923,21624924,21624925,21624926,21624927,21624960,21624961,21624962,21624963,21624964,21624965,21624966,21624967,21624968,21624969,21624970,21624971,21624972,21624973,21624974,21624975,21624976,21624977,21624978,21624979,21624980,21624981,21624982,21624983,21624984,21624985,21624986,21624987,21624988,21624989,21624990,21624991,21624992,21624993,21624994,21624995,21624996,21624997,21624998,21624999,21625000,21625001,21625002,21625003,21625004,21625005,21625006,21625007,21625008,21625009,21625010,21625011,21625012,21625013,21625014,21625015,21625016,21625017,21625018,21625019,21625020,21625021,21625022,21625023,21625024,21625040,21625041,21625042,21625043,21625044,21625045,21625046,21625047,21625048,21625049,21625050,21625051,21625052,21625053,21625054,21625055],"Polished Blackstone Bricks":[21619830],"Polished Blackstone Button":[21607152,21607153,21607154,21607155,21607156,21607157,21607160,21607161,21607162,21607163,21607164,21607165],"Polished Blackstone Pressure Plate":[21609892,21609893],"Polished Blackstone Slab":[21611892,21611893,21611894],"Polished Blackstone Stairs":[21614000,21614001,21614002,21614003,21614004,21614005,21614006,21614007],"Polished Blackstone Wall":[21614848,21614849,21614850,21614851,21614852,21614853,21614854,21614855,21614856,21614857,21614858,21614859,21614860,21614861,21614862,21614863,21614864,21614865,21614866,21614867,21614868,21614869,21614870,21614871,21614872,21614873,21614874,21614875,21614876,21614877,21614878,21614879,21614880,21614881,21614882,21614883,21614884,21614885,21614886,21614887,21614888,21614889,21614890,21614891,21614892,21614893,21614894,21614895,21614896,21614897,21614898,21614899,21614900,21614901,21614902,21614903,21614904,21614905,21614906,21614907,21614908,21614909,21614910,21614911,21614917,21614928,21614929,21614930,21614931,21614932,21614933,21614934,21614935,21614936,21614937,21614938,21614939,21614940,21614941,21614942,21614943,21614976,21614977,21614978,21614979,21614980,21614981,21614982,21614983,21614984,21614985,21614986,21614987,21614988,21614989,21614990,21614991,21614992,21614993,21614994,21614995,21614996,21614997,21614998,21614999,21615000,21615001,21615002,21615003,21615004,21615005,21615006,21615007,21615008,21615009,21615010,21615011,21615012,21615013,21615014,21615015,21615016,21615017,21615018,21615019,21615020,21615021,21615022,21615023,21615024,21615025,21615026,21615027,21615028,21615029,21615030,21615031,21615032,21615033,21615034,21615035,21615036,21615037,21615038,21615039,21615045,21615056,21615057,21615058,21615059,21615060,21615061,21615062,21615063,21615064,21615065,21615066,21615067,21615068,21615069,21615070,21615071],"Polished Deepslate":[21670363],"Polished Deepslate Slab":[21672268,21672270,21672271],"Polished Deepslate Stairs":[21674288,21674289,21674290,21674291,21674292,21674293,21674294,21674295],"Polished Deepslate Wall":[21676076,21676080,21676081,21676082,21676083,21676084,21676085,21676086,21676087,21676088,21676089,21676090,21676091,21676092,21676093,21676094,21676095,21676096,21676097,21676098,21676099,21676100,21676101,21676102,21676103,21676104,21676105,21676106,21676107,21676108,21676109,21676110,21676111,21676112,21676113,21676114,21676115,21676116,21676117,21676118,21676119,21676120,21676121,21676122,21676123,21676124,21676125,21676126,21676127,21676128,21676129,21676130,21676131,21676132,21676133,21676134,21676135,21676136,21676137,21676138,21676139,21676140,21676141,21676142,21676143,21676144,21676145,21676146,21676147,21676148,21676149,21676150,21676151,21676152,21676153,21676154,21676155,21676156,21676157,21676158,21676159,21676204,21676208,21676209,21676210,21676211,21676212,21676213,21676214,21676215,21676216,21676217,21676218,21676219,21676220,21676221,21676222,21676223,21676224,21676225,21676226,21676227,21676228,21676229,21676230,21676231,21676232,21676233,21676234,21676235,21676236,21676237,21676238,21676239,21676240,21676241,21676242,21676243,21676244,21676245,21676246,21676247,21676248,21676249,21676250,21676251,21676252,21676253,21676254,21676255,21676256,21676257,21676258,21676259,21676260,21676261,21676262,21676263,21676264,21676265,21676266,21676267,21676268,21676269,21676270,21676271,21676272,21676273,21676274,21676275,21676276,21676277,21676278,21676279,21676280,21676281,21676282,21676283,21676284,21676285,21676286,21676287],"Polished Diorite":[21298589],"Polished Diorite Slab":[21299568,21299570,21299571],"Polished Diorite Stairs":[21301328,21301329,21301330,21301331,21301332,21301333,21301334,21301335],"Polished Granite":[21304978],"Polished Granite Slab":[21307024,21307025,21307027],"Polished Granite Stairs":[21307536,21307537,21307538,21307539,21307540,21307541,21307542,21307543],"Polonium":[20915922],"Poppy":[21310628],"Potassium":[20916939],"Potato Block":[21312968,21312969,21312970,21312971,21312972,21312973,21312974,21312975],"Potion Cauldron":[21859216,21859217,21859218,21859219,21859220,21859221],"Powered Rail":[21313936,21313937,21313938,21313939,21313942,21313943,21313944,21313945,21313946,21313947,21313950,21313951],"Praseodymium":[20918290],"Prismarine":[21317514],"Prismarine Bricks":[21318880],"Prismarine Bricks Slab":[21319764,21319766,21319767],"Prismarine Bricks Stairs":[21322480,21322481,21322482,21322483,21322484,21322485,21322486,21322487],"Prismarine Slab":[21324568,21324569,21324571],"Prismarine Stairs":[21327832,21327833,21327834,21327835,21327836,21327837,21327838,21327839],"Prismarine Wall":[21327872,21327873,21327874,21327875,21327876,21327877,21327878,21327879,21327880,21327881,21327882,21327883,21327884,21327885,21327886,21327887,21327888,21327889,21327890,21327891,21327892,21327893,21327894,21327895,21327896,21327897,21327898,21327899,21327900,21327901,21327902,21327903,21327904,21327905,21327906,21327907,21327908,21327909,21327910,21327911,21327912,21327913,21327914,21327915,21327916,21327917,21327918,21327919,21327920,21327921,21327922,21327923,21327924,21327925,21327926,21327927,21327928,21327929,21327930,21327931,21327932,21327933,21327934,21327935,21327974,21327984,21327985,21327986,21327987,21327988,21327989,21327990,21327991,21327992,21327993,21327994,21327995,21327996,21327997,21327998,21327999,21328000,21328001,21328002,21328003,21328004,21328005,21328006,21328007,21328008,21328009,21328010,21328011,21328012,21328013,21328014,21328015,21328016,21328017,21328018,21328019,21328020,21328021,21328022,21328023,21328024,21328025,21328026,21328027,21328028,21328029,21328030,21328031,21328032,21328033,21328034,21328035,21328036,21328037,21328038,21328039,21328040,21328041,21328042,21328043,21328044,21328045,21328046,21328047,21328048,21328049,21328050,21328051,21328052,21328053,21328054,21328055,21328056,21328057,21328058,21328059,21328060,21328061,21328062,21328063,21328102,21328112,21328113,21328114,21328115,21328116,21328117,21328118,21328119,21328120,21328121,21328122,21328123,21328124,21328125,21328126,21328127],"Promethium":[20920321],"Protactinium":[20923161],"Pumpkin":[21331624],"Pumpkin Stem":[21333968,21333969,21333970,21333971,21333972,21333973,21333974,21333975,21333976,21333977,21333978,21333979,21333980,21333981,21333982,21333983,21333984,21333985,21333986,21333987,21333988,21333989,21333990,21333991,21333992,21333993,21333994,21333995,21333996,21333997,21333998,21333999,21334000,21334001,21334002,21334003,21334004,21334005,21334006,21334007],"Purple Torch":[21337610,21337611,21337612,21337613,21337614],"Purpur Block":[21338315],"Purpur Pillar":[21340545,21340546,21340547],"Purpur Slab":[21344168,21344169,21344170],"Purpur Stairs":[21344432,21344433,21344434,21344435,21344436,21344437,21344438,21344439],"Quartz Block":[21347391],"Quartz Bricks":[21678669],"Quartz Pillar":[21348820,21348821,21348823],"Quartz Slab":[21351776,21351777,21351779],"Quartz Stairs":[21352904,21352905,21352906,21352907,21352908,21352909,21352910,21352911],"Radium":[20925466],"Radon":[20927914],"Rail":[21356230,21356231,21356232,21356233,21356234,21356235,21356236,21356237,21356238,21356239],"Raw Copper Block":[21632127],"Raw Gold Block":[21634601],"Raw Iron Block":[21635367],"Red Mushroom":[21359866],"Red Mushroom Block":[21361105,21361106,21361107,21361112,21361113,21361114,21361115,21361116,21361117,21361118,21361119],"Red Nether Brick Slab":[21363940,21363942,21363943],"Red Nether Brick Stairs":[21365640,21365641,21365642,21365643,21365644,21365645,21365646,21365647],"Red Nether Brick Wall":[21367808,21367809,21367810,21367811,21367812,21367813,21367814,21367815,21367816,21367817,21367818,21367819,21367820,21367821,21367822,21367823,21367824,21367825,21367826,21367827,21367828,21367829,21367830,21367831,21367832,21367833,21367834,21367835,21367836,21367837,21367838,21367839,21367840,21367841,21367842,21367843,21367844,21367845,21367846,21367847,21367848,21367849,21367850,21367851,21367852,21367853,21367854,21367855,21367856,21367857,21367858,21367859,21367860,21367861,21367862,21367863,21367864,21367865,21367866,21367867,21367868,21367869,21367870,21367871,21367872,21367888,21367889,21367890,21367891,21367892,21367893,21367894,21367895,21367896,21367897,21367898,21367899,21367900,21367901,21367902,21367903,21367936,21367937,21367938,21367939,21367940,21367941,21367942,21367943,21367944,21367945,21367946,21367947,21367948,21367949,21367950,21367951,21367952,21367953,21367954,21367955,21367956,21367957,21367958,21367959,21367960,21367961,21367962,21367963,21367964,21367965,21367966,21367967,21367968,21367969,21367970,21367971,21367972,21367973,21367974,21367975,21367976,21367977,21367978,21367979,21367980,21367981,21367982,21367983,21367984,21367985,21367986,21367987,21367988,21367989,21367990,21367991,21367992,21367993,21367994,21367995,21367996,21367997,21367998,21367999,21368000,21368016,21368017,21368018,21368019,21368020,21368021,21368022,21368023,21368024,21368025,21368026,21368027,21368028,21368029,21368030,21368031],"Red Nether Bricks":[21368920],"Red Sand":[21371243],"Red Sandstone":[21373682],"Red Sandstone Slab":[21375308,21375309,21375310],"Red Sandstone Stairs":[21377256,21377257,21377258,21377259,21377260,21377261,21377262,21377263],"Red Sandstone Wall":[21380352,21380353,21380354,21380355,21380356,21380357,21380358,21380359,21380360,21380361,21380362,21380363,21380364,21380365,21380366,21380367,21380383,21380416,21380417,21380418,21380419,21380420,21380421,21380422,21380423,21380424,21380425,21380426,21380427,21380428,21380429,21380430,21380431,21380432,21380433,21380434,21380435,21380436,21380437,21380438,21380439,21380440,21380441,21380442,21380443,21380444,21380445,21380446,21380447,21380448,21380449,21380450,21380451,21380452,21380453,21380454,21380455,21380456,21380457,21380458,21380459,21380460,21380461,21380462,21380463,21380464,21380465,21380466,21380467,21380468,21380469,21380470,21380471,21380472,21380473,21380474,21380475,21380476,21380477,21380478,21380479,21380480,21380481,21380482,21380483,21380484,21380485,21380486,21380487,21380488,21380489,21380490,21380491,21380492,21380493,21380494,21380495,21380511,21380544,21380545,21380546,21380547,21380548,21380549,21380550,21380551,21380552,21380553,21380554,21380555,21380556,21380557,21380558,21380559,21380560,21380561,21380562,21380563,21380564,21380565,21380566,21380567,21380568,21380569,21380570,21380571,21380572,21380573,21380574,21380575,21380576,21380577,21380578,21380579,21380580,21380581,21380582,21380583,21380584,21380585,21380586,21380587,21380588,21380589,21380590,21380591,21380592,21380593,21380594,21380595,21380596,21380597,21380598,21380599,21380600,21380601,21380602,21380603,21380604,21380605,21380606,21380607],"Red Torch":[21381490,21381491,21381492,21381493,21381495],"Red Tulip":[21384139],"Redstone":[21397872,21397873,21397874,21397875,21397876,21397877,21397878,21397879,21397880,21397881,21397882,21397883,21397884,21397885,21397886,21397887],"Redstone Block":[21385654],"Redstone Comparator":[21388928,21388929,21388930,21388931,21388932,21388933,21388934,21388935,21388936,21388937,21388938,21388939,21388940,21388941,21388942,21388943],"Redstone Lamp":[21390644,21390645],"Redstone Ore":[21392016,21392017],"Redstone Repeater":[21394976,21394977,21394978,21394979,21394980,21394981,21394982,21394983,21394984,21394985,21394986,21394987,21394988,21394989,21394990,21394991,21394992,21394993,21394994,21394995,21394996,21394997,21394998,21394999,21395000,21395001,21395002,21395003,21395004,21395005,21395006,21395007],"Redstone Torch":[21396497,21396498,21396499,21396500,21396501,21396505,21396506,21396507,21396508,21396509],"Reinforced Deepslate":[21889612],"Rhenium":[20930190],"Rhodium":[20931608],"Roentgenium":[20934217],"Rose Bush":[21402408,21402409],"Rubidium":[20936670],"Ruthenium":[20937111],"Rutherfordium":[20939938],"Samarium":[20942830],"Sand":[21405599],"Sandstone":[21406854],"Sandstone Slab":[21408357,21408358,21408359],"Sandstone Stairs":[21410360,21410361,21410362,21410363,21410364,21410365,21410366,21410367],"Sandstone Wall":[21413632,21413633,21413634,21413635,21413636,21413637,21413638,21413639,21413640,21413641,21413642,21413643,21413644,21413645,21413646,21413647,21413648,21413649,21413650,21413651,21413652,21413653,21413654,21413655,21413656,21413657,21413658,21413659,21413660,21413661,21413662,21413663,21413664,21413665,21413666,21413667,21413668,21413669,21413670,21413671,21413672,21413673,21413674,21413675,21413676,21413677,21413678,21413679,21413680,21413681,21413682,21413683,21413684,21413685,21413686,21413687,21413688,21413689,21413690,21413691,21413692,21413693,21413694,21413695,21413696,21413697,21413698,21413699,21413700,21413701,21413702,21413703,21413704,21413705,21413706,21413707,21413708,21413709,21413710,21413711,21413719,21413760,21413761,21413762,21413763,21413764,21413765,21413766,21413767,21413768,21413769,21413770,21413771,21413772,21413773,21413774,21413775,21413776,21413777,21413778,21413779,21413780,21413781,21413782,21413783,21413784,21413785,21413786,21413787,21413788,21413789,21413790,21413791,21413792,21413793,21413794,21413795,21413796,21413797,21413798,21413799,21413800,21413801,21413802,21413803,21413804,21413805,21413806,21413807,21413808,21413809,21413810,21413811,21413812,21413813,21413814,21413815,21413816,21413817,21413818,21413819,21413820,21413821,21413822,21413823,21413824,21413825,21413826,21413827,21413828,21413829,21413830,21413831,21413832,21413833,21413834,21413835,21413836,21413837,21413838,21413839,21413847],"Scandium":[20944835],"Sculk":[21880279],"Sea Lantern":[21415151],"Sea Pickle":[21417344,21417345,21417346,21417347,21417348,21417349,21417350,21417351],"Seaborgium":[20946478],"Selenium":[20948963],"Shroomlight":[21696604],"Shulker Box":[21418259],"Silicon":[20949447],"Silver":[20952835],"Slime Block":[21421890],"Small Dripleaf":[21927576,21927577,21927578,21927579,21927580,21927581,21927582,21927583],"Smithing Table":[21847863],"Smoker":[21423456,21423457,21423458,21423459,21423460,21423461,21423462,21423463],"Smooth Basalt":[21595261],"Smooth Quartz Block":[21425097],"Smooth Quartz Slab":[21428056,21428057,21428058],"Smooth Quartz Stairs":[21429504,21429505,21429506,21429507,21429508,21429509,21429510,21429511],"Smooth Red Sandstone":[21431324],"Smooth Red Sandstone Slab":[21432877,21432878,21432879],"Smooth Red Sandstone Stairs":[21436184,21436185,21436186,21436187,21436188,21436189,21436190,21436191],"Smooth Sandstone":[21438242],"Smooth Sandstone Slab":[21439385,21439386,21439387],"Smooth Sandstone Stairs":[21440752,21440753,21440754,21440755,21440756,21440757,21440758,21440759],"Smooth Stone":[21444566],"Smooth Stone Slab":[21446216,21446217,21446219],"Snow Block":[21448652],"Snow Layer":[21449736,21449737,21449738,21449739,21449740,21449741,21449742,21449743],"Sodium":[20953522],"Soul Fire":[21696136],"Soul Lantern":[21692144,21692145],"Soul Sand":[21451142],"Soul Soil":[21693270],"Soul Torch":[21689448,21689449,21689452,21689454,21689455],"Sponge":[21454292,21454293],"Spore Blossom":[21852156],"Spruce Button":[21456688,21456689,21456690,21456691,21456692,21456693,21456696,21456697,21456698,21456699,21456700,21456701],"Spruce Door":[21458400,21458401,21458402,21458403,21458404,21458405,21458406,21458407,21458408,21458409,21458410,21458411,21458412,21458413,21458414,21458415,21458416,21458417,21458418,21458419,21458420,21458421,21458422,21458423,21458424,21458425,21458426,21458427,21458428,21458429,21458430,21458431],"Spruce Fence":[21459375],"Spruce Fence Gate":[21462512,21462513,21462514,21462515,21462516,21462517,21462518,21462519,21462520,21462521,21462522,21462523,21462524,21462525,21462526,21462527],"Spruce Leaves":[21463656,21463657,21463658,21463659],"Spruce Log":[21465720,21465721,21465724,21465725,21465726,21465727],"Spruce Planks":[21468562],"Spruce Pressure Plate":[21469450,21469451],"Spruce Sapling":[21472860,21472861],"Spruce Sign":[21473424,21473425,21473426,21473427,21473428,21473429,21473430,21473431,21473432,21473433,21473434,21473435,21473436,21473437,21473438,21473439],"Spruce Slab":[21475816,21475817,21475818],"Spruce Stairs":[21477456,21477457,21477458,21477459,21477460,21477461,21477462,21477463],"Spruce Trapdoor":[21481008,21481009,21481010,21481011,21481012,21481013,21481014,21481015,21481016,21481017,21481018,21481019,21481020,21481021,21481022,21481023],"Spruce Wall Sign":[21483504,21483505,21483506,21483507],"Spruce Wood":[21484912,21484913,21484914,21484915,21484918,21484919],"Stained Clay":[21486144,21486145,21486146,21486147,21486148,21486149,21486150,21486151,21486152,21486153,21486154,21486155,21486156,21486157,21486158,21486159],"Stained Glass":[21489584,21489585,21489586,21489587,21489588,21489589,21489590,21489591,21489592,21489593,21489594,21489595,21489596,21489597,21489598,21489599],"Stained Glass Pane":[21489792,21489793,21489794,21489795,21489796,21489797,21489798,21489799,21489800,21489801,21489802,21489803,21489804,21489805,21489806,21489807],"Stained Hardened Glass":[21491728,21491729,21491730,21491731,21491732,21491733,21491734,21491735,21491736,21491737,21491738,21491739,21491740,21491741,21491742,21491743],"Stained Hardened Glass Pane":[21495136,21495137,21495138,21495139,21495140,21495141,21495142,21495143,21495144,21495145,21495146,21495147,21495148,21495149,21495150,21495151],"Stone":[21496052],"Stone Brick Slab":[21498972,21498973,21498974],"Stone Brick Stairs":[21500248,21500249,21500250,21500251,21500252,21500253,21500254,21500255],"Stone Brick Wall":[21503744,21503745,21503746,21503747,21503748,21503749,21503750,21503751,21503752,21503753,21503754,21503755,21503756,21503757,21503758,21503759,21503773,21503808,21503809,21503810,21503811,21503812,21503813,21503814,21503815,21503816,21503817,21503818,21503819,21503820,21503821,21503822,21503823,21503824,21503825,21503826,21503827,21503828,21503829,21503830,21503831,21503832,21503833,21503834,21503835,21503836,21503837,21503838,21503839,21503840,21503841,21503842,21503843,21503844,21503845,21503846,21503847,21503848,21503849,21503850,21503851,21503852,21503853,21503854,21503855,21503856,21503857,21503858,21503859,21503860,21503861,21503862,21503863,21503864,21503865,21503866,21503867,21503868,21503869,21503870,21503871,21503872,21503873,21503874,21503875,21503876,21503877,21503878,21503879,21503880,21503881,21503882,21503883,21503884,21503885,21503886,21503887,21503901,21503936,21503937,21503938,21503939,21503940,21503941,21503942,21503943,21503944,21503945,21503946,21503947,21503948,21503949,21503950,21503951,21503952,21503953,21503954,21503955,21503956,21503957,21503958,21503959,21503960,21503961,21503962,21503963,21503964,21503965,21503966,21503967,21503968,21503969,21503970,21503971,21503972,21503973,21503974,21503975,21503976,21503977,21503978,21503979,21503980,21503981,21503982,21503983,21503984,21503985,21503986,21503987,21503988,21503989,21503990,21503991,21503992,21503993,21503994,21503995,21503996,21503997,21503998,21503999],"Stone Bricks":[21505414],"Stone Button":[21507712,21507713,21507714,21507715,21507718,21507719,21507720,21507721,21507722,21507723,21507726,21507727],"Stone Pressure Plate":[21508356,21508357],"Stone Slab":[21512017,21512018,21512019],"Stone Stairs":[21512192,21512193,21512194,21512195,21512196,21512197,21512198,21512199],"Stonecutter":[21514984,21514985,21514986,21514987],"Strontium":[20955164],"Sugarcane":[21542656,21542657,21542658,21542659,21542660,21542661,21542662,21542663,21542664,21542665,21542666,21542667,21542668,21542669,21542670,21542671],"Sulfur":[20958068],"Sunflower":[21544506,21544507],"Sweet Berry Bush":[21545764,21545765,21545766,21545767],"TNT":[21550004,21550005,21550006,21550007],"Tall Grass":[21547986],"Tantalum":[20959931],"Technetium":[20962279],"Tellurium":[20964192],"Tennessine":[20965501],"Terbium":[20968631],"Thallium":[20971328],"Thorium":[20973507],"Thulium":[20974346],"Tin":[20976139],"Tinted Glass":[21779734],"Titanium":[20978911],"Torch":[21551770,21551771,21551772,21551773,21551775],"Torchflower":[21940847],"Torchflower Crop":[21943796,21943797],"Trapped Chest":[21553240,21553241,21553242,21553243],"Tripwire":[21556976,21556977,21556978,21556979,21556980,21556981,21556982,21556983,21556984,21556985,21556986,21556987,21556988,21556989,21556990,21556991],"Tripwire Hook":[21559008,21559009,21559010,21559011,21559012,21559013,21559014,21559015,21559016,21559017,21559018,21559019,21559020,21559021,21559022,21559023],"Tuff":[21688162],"Tungsten":[20981723],"Twisting Vines":[21873664,21873665,21873666,21873667,21873668,21873669,21873670,21873671,21873672,21873673,21873674,21873675,21873676,21873677,21873678,21873679,21873680,21873681,21873688,21873689,21873690,21873691,21873692,21873693,21873694,21873695],"Underwater Torch":[21560248,21560249,21560252,21560254,21560255],"Uranium":[20982778],"Vanadium":[20984123],"Vines":[21562368,21562369,21562370,21562371,21562372,21562373,21562374,21562375,21562376,21562377,21562378,21562379,21562380,21562381,21562382,21562383],"Wall Banner":[21564672,21564673,21564674,21564675,21564676,21564677,21564678,21564679,21564680,21564681,21564682,21564683,21564684,21564685,21564686,21564687,21564688,21564689,21564690,21564691,21564692,21564693,21564694,21564695,21564696,21564697,21564698,21564699,21564700,21564701,21564702,21564703,21564704,21564705,21564706,21564707,21564708,21564709,21564710,21564711,21564712,21564713,21564714,21564715,21564716,21564717,21564718,21564719,21564720,21564721,21564722,21564723,21564724,21564725,21564726,21564727,21564728,21564729,21564730,21564731,21564732,21564733,21564734,21564735],"Wall Coral Fan":[21567296,21567300,21567301,21567302,21567303,21567304,21567308,21567309,21567310,21567311,21567312,21567316,21567317,21567318,21567319,21567320,21567324,21567325,21567326,21567327,21567328,21567332,21567333,21567334,21567335,21567336,21567340,21567341,21567342,21567343,21567344,21567348,21567349,21567350,21567351,21567352,21567356,21567357,21567358,21567359],"Warped Button":[21741552,21741553,21741556,21741557,21741558,21741559,21741560,21741561,21741564,21741565,21741566,21741567],"Warped Door":[21752672,21752673,21752674,21752675,21752676,21752677,21752678,21752679,21752680,21752681,21752682,21752683,21752684,21752685,21752686,21752687,21752688,21752689,21752690,21752691,21752692,21752693,21752694,21752695,21752696,21752697,21752698,21752699,21752700,21752701,21752702,21752703],"Warped Fence":[21709978],"Warped Fence Gate":[21759616,21759617,21759618,21759619,21759620,21759621,21759622,21759623,21759624,21759625,21759626,21759627,21759628,21759629,21759630,21759631],"Warped Hyphae":[21727976,21727977,21727980,21727981,21727982,21727983],"Warped Planks":[21703045],"Warped Pressure Plate":[21747528,21747529],"Warped Roots":[21938097],"Warped Sign":[21771312,21771313,21771314,21771315,21771316,21771317,21771318,21771319,21771320,21771321,21771322,21771323,21771324,21771325,21771326,21771327],"Warped Slab":[21715036,21715038,21715039],"Warped Stairs":[21765928,21765929,21765930,21765931,21765932,21765933,21765934,21765935],"Warped Stem":[21721496,21721497,21721500,21721501,21721502,21721503],"Warped Trapdoor":[21734544,21734545,21734546,21734547,21734548,21734549,21734550,21734551,21734552,21734553,21734554,21734555,21734556,21734557,21734558,21734559],"Warped Wall Sign":[21777200,21777201,21777202,21777203],"Warped Wart Block":[21815813],"Water":[21568608,21568609,21568610,21568611,21568612,21568613,21568614,21568615,21568616,21568617,21568618,21568619,21568620,21568621,21568622,21568623,21568624,21568625,21568626,21568627,21568628,21568629,21568630,21568631,21568632,21568633,21568634,21568635,21568636,21568637,21568638,21568639],"Water Cauldron":[21854480,21854481,21854482,21854483,21854486,21854487],"Weeping Vines":[21875072,21875073,21875074,21875075,21875076,21875077,21875078,21875079,21875080,21875081,21875082,21875083,21875084,21875085,21875086,21875087,21875088,21875089,21875090,21875091,21875092,21875093,21875094,21875095,21875102,21875103],"Weighted Pressure Plate Heavy":[21570544,21570545,21570546,21570547,21570548,21570549,21570550,21570551,21570552,21570553,21570554,21570555,21570556,21570557,21570558,21570559],"Weighted Pressure Plate Light":[21572160,21572161,21572162,21572163,21572164,21572165,21572166,21572167,21572168,21572169,21572170,21572171,21572172,21572173,21572174,21572175],"Wheat Block":[21575552,21575553,21575554,21575555,21575556,21575557,21575558,21575559],"White Tulip":[21578492],"Wither Rose":[21841807],"Wool":[21580784,21580785,21580786,21580787,21580788,21580789,21580790,21580791,21580792,21580793,21580794,21580795,21580796,21580797,21580798,21580799],"Xenon":[20987135],"Ytterbium":[20989425],"Yttrium":[20991002],"Zinc":[20995928],"Zirconium":[20996890],"ate!upd":[21097998],"reserved6":[21399796],"update!":[21096340]},"stateDataBits":11}
\ No newline at end of file
diff --git a/tests/phpunit/data/bedrock/DyeColorIdMapTest.php b/tests/phpunit/data/bedrock/DyeColorIdMapTest.php
index b5561019faa..a3ca6db25ce 100644
--- a/tests/phpunit/data/bedrock/DyeColorIdMapTest.php
+++ b/tests/phpunit/data/bedrock/DyeColorIdMapTest.php
@@ -29,10 +29,10 @@
 class DyeColorIdMapTest extends TestCase{
 
 	public function testAllColorsMapped() : void{
-		foreach(DyeColor::getAll() as $color){
+		foreach(DyeColor::cases() as $color){
 			$id = DyeColorIdMap::getInstance()->toId($color);
 			$color2 = DyeColorIdMap::getInstance()->fromId($id);
-			self::assertTrue($color2 !== null && $color->equals($color2));
+			self::assertTrue($color === $color2);
 		}
 	}
 }
diff --git a/tests/phpunit/data/bedrock/block/convert/BlockSerializerDeserializerTest.php b/tests/phpunit/data/bedrock/block/convert/BlockSerializerDeserializerTest.php
index 6db39bb48a8..a47a9b155ad 100644
--- a/tests/phpunit/data/bedrock/block/convert/BlockSerializerDeserializerTest.php
+++ b/tests/phpunit/data/bedrock/block/convert/BlockSerializerDeserializerTest.php
@@ -28,6 +28,7 @@
 use pocketmine\block\Bed;
 use pocketmine\block\BlockTypeIds;
 use pocketmine\block\CaveVines;
+use pocketmine\block\Farmland;
 use pocketmine\block\MobHead;
 use pocketmine\block\RuntimeBlockStateRegistry;
 use pocketmine\data\bedrock\block\BlockStateDeserializeException;
@@ -76,6 +77,8 @@ public function testAllKnownBlockStatesSerializableAndDeserializable() : void{
 				$newBlock->setMobHeadType($block->getMobHeadType());
 			}elseif($block instanceof CaveVines && $newBlock instanceof CaveVines && !$block->hasBerries()){
 				$newBlock->setHead($block->isHead());
+			}elseif($block instanceof Farmland && $newBlock instanceof Farmland){
+				$block->setWaterPositionIndex($newBlock->getWaterPositionIndex());
 			}
 
 			self::assertSame($block->getStateId(), $newBlock->getStateId(), "Mismatch of blockstate for " . $block->getName() . ", " . print_r($block, true) . " vs " . print_r($newBlock, true));
diff --git a/tests/phpunit/item/BannerTest.php b/tests/phpunit/item/BannerTest.php
index c555031a093..797ca60fcaa 100644
--- a/tests/phpunit/item/BannerTest.php
+++ b/tests/phpunit/item/BannerTest.php
@@ -36,7 +36,7 @@ public function testBannerPatternSaveRestore() : void{
 		$item = VanillaBlocks::BANNER()->asItem();
 		assert($item instanceof Banner);
 		$item->setPatterns([
-			new BannerPatternLayer(BannerPatternType::FLOWER(), DyeColor::RED())
+			new BannerPatternLayer(BannerPatternType::FLOWER, DyeColor::RED)
 		]);
 		$data = $item->nbtSerialize();
 
@@ -45,6 +45,6 @@ public function testBannerPatternSaveRestore() : void{
 		self::assertInstanceOf(Banner::class, $item2);
 		$patterns = $item2->getPatterns();
 		self::assertCount(1, $patterns);
-		self::assertTrue(BannerPatternType::FLOWER()->equals($patterns[0]->getType()));
+		self::assertTrue(BannerPatternType::FLOWER === $patterns[0]->getType());
 	}
 }