Merge branch 'minor-next' into stable

This commit is contained in:
Dylan K. Taylor 2023-07-04 14:43:23 +01:00
commit 02ffb04b92
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
49 changed files with 1552 additions and 565 deletions

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\build\generate_item_serializer_ids; namespace pocketmine\build\generate_item_serializer_ids;
use pocketmine\data\bedrock\item\BlockItemIdMap;
use pocketmine\errorhandler\ErrorToExceptionHandler; use pocketmine\errorhandler\ErrorToExceptionHandler;
use pocketmine\network\mcpe\convert\ItemTypeDictionaryFromDataHelper; use pocketmine\network\mcpe\convert\ItemTypeDictionaryFromDataHelper;
use pocketmine\network\mcpe\protocol\serializer\ItemTypeDictionary; use pocketmine\network\mcpe\protocol\serializer\ItemTypeDictionary;
@ -45,10 +46,10 @@ function constifyMcId(string $id) : string{
return strtoupper(explode(":", $id, 2)[1]); return strtoupper(explode(":", $id, 2)[1]);
} }
function generateItemIds(ItemTypeDictionary $dictionary) : void{ function generateItemIds(ItemTypeDictionary $dictionary, BlockItemIdMap $blockItemIdMap) : void{
$ids = []; $ids = [];
foreach($dictionary->getEntries() as $entry){ foreach($dictionary->getEntries() as $entry){
if($entry->getNumericId() < 256){ //blockitems are serialized via BlockStateSerializer if($entry->getStringId() === "minecraft:air" || $blockItemIdMap->lookupBlockId($entry->getStringId()) !== null){ //blockitems are serialized via BlockStateSerializer
continue; continue;
} }
$ids[$entry->getStringId()] = $entry->getStringId(); $ids[$entry->getStringId()] = $entry->getStringId();
@ -92,6 +93,7 @@ if($raw === false){
} }
$dictionary = ItemTypeDictionaryFromDataHelper::loadFromString($raw); $dictionary = ItemTypeDictionaryFromDataHelper::loadFromString($raw);
generateItemIds($dictionary); $blockItemIdMap = BlockItemIdMap::getInstance();
generateItemIds($dictionary, $blockItemIdMap);
echo "Done. Don't forget to run CS fixup after generating code.\n"; echo "Done. Don't forget to run CS fixup after generating code.\n";

View File

@ -22,7 +22,7 @@
"ext-openssl": "*", "ext-openssl": "*",
"ext-pcre": "*", "ext-pcre": "*",
"ext-phar": "*", "ext-phar": "*",
"ext-pmmpthread": "^6.0.1", "ext-pmmpthread": "^6.0.4",
"ext-reflection": "*", "ext-reflection": "*",
"ext-simplexml": "*", "ext-simplexml": "*",
"ext-sockets": "*", "ext-sockets": "*",
@ -34,7 +34,7 @@
"adhocore/json-comment": "~1.2.0", "adhocore/json-comment": "~1.2.0",
"fgrosse/phpasn1": "~2.5.0", "fgrosse/phpasn1": "~2.5.0",
"pocketmine/netresearch-jsonmapper": "~v4.2.999", "pocketmine/netresearch-jsonmapper": "~v4.2.999",
"pocketmine/bedrock-block-upgrade-schema": "~2.2.0+bedrock-1.20.0", "pocketmine/bedrock-block-upgrade-schema": "~3.0.0",
"pocketmine/bedrock-data": "~2.3.0+bedrock-1.20.0", "pocketmine/bedrock-data": "~2.3.0+bedrock-1.20.0",
"pocketmine/bedrock-item-upgrade-schema": "~1.3.0+bedrock-1.20.0", "pocketmine/bedrock-item-upgrade-schema": "~1.3.0+bedrock-1.20.0",
"pocketmine/bedrock-protocol": "~22.0.0+bedrock-1.20.0", "pocketmine/bedrock-protocol": "~22.0.0+bedrock-1.20.0",

16
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "3b102702eabc31fd8b2651db6c259397", "content-hash": "ff0fa864a7853011bf4620d870c47ac7",
"packages": [ "packages": [
{ {
"name": "adhocore/json-comment", "name": "adhocore/json-comment",
@ -198,16 +198,16 @@
}, },
{ {
"name": "pocketmine/bedrock-block-upgrade-schema", "name": "pocketmine/bedrock-block-upgrade-schema",
"version": "2.2.0", "version": "3.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git", "url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git",
"reference": "79bb3ad542ef19e828fdf1fa6adc54f1fa4b3bb5" "reference": "8b72c47109e174ac7f17c3ac546748f8e49a5fdf"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/79bb3ad542ef19e828fdf1fa6adc54f1fa4b3bb5", "url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/8b72c47109e174ac7f17c3ac546748f8e49a5fdf",
"reference": "79bb3ad542ef19e828fdf1fa6adc54f1fa4b3bb5", "reference": "8b72c47109e174ac7f17c3ac546748f8e49a5fdf",
"shasum": "" "shasum": ""
}, },
"type": "library", "type": "library",
@ -218,9 +218,9 @@
"description": "Schemas describing how to upgrade saved block data in older Minecraft: Bedrock Edition world saves", "description": "Schemas describing how to upgrade saved block data in older Minecraft: Bedrock Edition world saves",
"support": { "support": {
"issues": "https://github.com/pmmp/BedrockBlockUpgradeSchema/issues", "issues": "https://github.com/pmmp/BedrockBlockUpgradeSchema/issues",
"source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/2.2.0" "source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/3.0.0"
}, },
"time": "2023-05-04T21:49:36+00:00" "time": "2023-07-03T16:35:44+00:00"
}, },
{ {
"name": "pocketmine/bedrock-data", "name": "pocketmine/bedrock-data",
@ -3020,7 +3020,7 @@
"ext-openssl": "*", "ext-openssl": "*",
"ext-pcre": "*", "ext-pcre": "*",
"ext-phar": "*", "ext-phar": "*",
"ext-pmmpthread": "^6.0.1", "ext-pmmpthread": "^6.0.4",
"ext-reflection": "*", "ext-reflection": "*",
"ext-simplexml": "*", "ext-simplexml": "*",
"ext-sockets": "*", "ext-sockets": "*",

View File

@ -120,8 +120,8 @@ namespace pocketmine {
} }
if(($pmmpthread_version = phpversion("pmmpthread")) !== false){ if(($pmmpthread_version = phpversion("pmmpthread")) !== false){
if(version_compare($pmmpthread_version, "6.0.1") < 0 || version_compare($pmmpthread_version, "7.0.0") >= 0){ if(version_compare($pmmpthread_version, "6.0.4") < 0 || version_compare($pmmpthread_version, "7.0.0") >= 0){
$messages[] = "pmmpthread ^6.0.1 is required, while you have $pmmpthread_version."; $messages[] = "pmmpthread ^6.0.4 is required, while you have $pmmpthread_version.";
} }
} }

View File

@ -31,7 +31,7 @@ use function str_repeat;
final class VersionInfo{ final class VersionInfo{
public const NAME = "PocketMine-MP"; public const NAME = "PocketMine-MP";
public const BASE_VERSION = "5.1.4"; public const BASE_VERSION = "5.2.0";
public const IS_DEVELOPMENT_BUILD = true; public const IS_DEVELOPMENT_BUILD = true;
public const BUILD_CHANNEL = "stable"; public const BUILD_CHANNEL = "stable";

View File

@ -717,8 +717,24 @@ final class BlockTypeIds{
public const FLOWERING_AZALEA_LEAVES = 10687; public const FLOWERING_AZALEA_LEAVES = 10687;
public const REINFORCED_DEEPSLATE = 10688; public const REINFORCED_DEEPSLATE = 10688;
public const CAVE_VINES = 10689; public const CAVE_VINES = 10689;
public const GLOW_LICHEN = 10690;
public const CHERRY_BUTTON = 10691;
public const CHERRY_DOOR = 10692;
public const CHERRY_FENCE = 10693;
public const CHERRY_FENCE_GATE = 10694;
public const CHERRY_LEAVES = 10695;
public const CHERRY_LOG = 10696;
public const CHERRY_PLANKS = 10697;
public const CHERRY_PRESSURE_PLATE = 10698;
public const CHERRY_SAPLING = 10699;
public const CHERRY_SIGN = 10700;
public const CHERRY_SLAB = 10701;
public const CHERRY_STAIRS = 10702;
public const CHERRY_TRAPDOOR = 10703;
public const CHERRY_WALL_SIGN = 10704;
public const CHERRY_WOOD = 10705;
public const FIRST_UNUSED_BLOCK_ID = 10690; public const FIRST_UNUSED_BLOCK_ID = 10706;
private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID; private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID;

284
src/block/GlowLichen.php Normal file
View File

@ -0,0 +1,284 @@
<?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\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\event\block\BlockSpreadEvent;
use pocketmine\item\Fertilizer;
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 pocketmine\world\World;
use function array_key_first;
use function count;
use function shuffle;
class GlowLichen extends Transparent{
/** @var int[] */
protected array $faces = [];
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->facingFlags($this->faces);
}
/** @return int[] */
public function getFaces() : array{ return $this->faces; }
public function hasFace(int $face) : bool{
return isset($this->faces[$face]);
}
/**
* @param int[] $faces
* @return $this
*/
public function setFaces(array $faces) : self{
$uniqueFaces = [];
foreach($faces as $face){
Facing::validate($face);
$uniqueFaces[$face] = $face;
}
$this->faces = $uniqueFaces;
return $this;
}
/** @return $this */
public function setFace(int $face, bool $value) : self{
Facing::validate($face);
if($value){
$this->faces[$face] = $face;
}else{
unset($this->faces[$face]);
}
return $this;
}
public function getLightLevel() : int{
return 7;
}
public function isSolid() : bool{
return false;
}
/**
* @return AxisAlignedBB[]
*/
protected function recalculateCollisionBoxes() : array{
return [];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE();
}
public function canBeReplaced() : bool{
return true;
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
$this->faces = $blockReplace instanceof GlowLichen ? $blockReplace->faces : [];
$availableFaces = $this->getAvailableFaces();
if(count($availableFaces) === 0){
return false;
}
$opposite = Facing::opposite($face);
$placedFace = isset($availableFaces[$opposite]) ? $opposite : array_key_first($availableFaces);
$this->faces[$placedFace] = $placedFace;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onNearbyBlockChange() : void{
$changed = false;
foreach($this->faces as $face){
if(!$this->getSide($face)->getSupportType(Facing::opposite($face))->equals(SupportType::FULL())){
unset($this->faces[$face]);
$changed = true;
}
}
if($changed){
$world = $this->position->getWorld();
if(count($this->faces) === 0){
$world->useBreakOn($this->position);
}else{
$world->setBlock($this->position, $this);
}
}
}
private function getSpreadBlock(Block $replace, int $spreadFace) : ?Block{
if($replace instanceof self && $replace->hasSameTypeId($this)){
if($replace->hasFace($spreadFace)){
return null;
}
$result = $replace;
}elseif($replace->getTypeId() === BlockTypeIds::AIR){
$result = VanillaBlocks::GLOW_LICHEN();
}else{
//TODO: if this is a water block, generate a waterlogged block
return null;
}
return $result->setFace($spreadFace, true);
}
private function spread(World $world, Vector3 $replacePos, int $spreadFace) : bool{
$supportBlock = $world->getBlock($replacePos->getSide($spreadFace));
$supportFace = Facing::opposite($spreadFace);
if(!$supportBlock->getSupportType($supportFace)->equals(SupportType::FULL())){
return false;
}
$replacedBlock = $supportBlock->getSide($supportFace);
$replacementBlock = $this->getSpreadBlock($replacedBlock, Facing::opposite($supportFace));
if($replacementBlock === null){
return false;
}
$ev = new BlockSpreadEvent($replacedBlock, $this, $replacementBlock);
$ev->call();
if(!$ev->isCancelled()){
$world->setBlock($replacedBlock->getPosition(), $ev->getNewState());
return true;
}
return false;
}
/**
* @phpstan-return \Generator<int, int, void, void>
*/
private static function getShuffledSpreadFaces(int $sourceFace) : \Generator{
$skipAxis = Facing::axis($sourceFace);
$faces = Facing::ALL;
shuffle($faces);
foreach($faces as $spreadFace){
if(Facing::axis($spreadFace) !== $skipAxis){
yield $spreadFace;
}
}
}
private function spreadAroundSupport(int $sourceFace) : bool{
$world = $this->position->getWorld();
$supportPos = $this->position->getSide($sourceFace);
foreach(self::getShuffledSpreadFaces($sourceFace) as $spreadFace){
$replacePos = $supportPos->getSide($spreadFace);
if($this->spread($world, $replacePos, Facing::opposite($spreadFace))){
return true;
}
}
return false;
}
private function spreadAdjacentToSupport(int $sourceFace) : bool{
$world = $this->position->getWorld();
foreach(self::getShuffledSpreadFaces($sourceFace) as $spreadFace){
$replacePos = $this->position->getSide($spreadFace);
if($this->spread($world, $replacePos, $sourceFace)){
return true;
}
}
return false;
}
private function spreadWithinSelf(int $sourceFace) : bool{
foreach(self::getShuffledSpreadFaces($sourceFace) as $spreadFace){
if(!$this->hasFace($spreadFace) && $this->spread($this->position->getWorld(), $this->position, $spreadFace)){
return true;
}
}
return false;
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($item instanceof Fertilizer && count($this->faces) > 0){
$shuffledFaces = $this->faces;
shuffle($shuffledFaces);
$spreadMethods = [
$this->spreadAroundSupport(...),
$this->spreadAdjacentToSupport(...),
$this->spreadWithinSelf(...),
];
shuffle($spreadMethods);
foreach($shuffledFaces as $sourceFace){
foreach($spreadMethods as $spreadMethod){
if($spreadMethod($sourceFace)){
$item->pop();
break 2;
}
}
}
return true;
}
return false;
}
public function getDrops(Item $item) : array{
if(($item->getBlockToolType() & BlockToolType::SHEARS) !== 0){
return $this->getDropsForCompatibleTool($item);
}
return [];
}
public function getFlameEncouragement() : int{
return 15;
}
public function getFlammability() : int{
return 100;
}
/**
* @return array<int, int> $faces
*/
private function getAvailableFaces() : array{
$faces = [];
foreach(Facing::ALL as $face){
if(!$this->hasFace($face) && $this->getSide($face)->getSupportType(Facing::opposite($face))->equals(SupportType::FULL())){
$faces[$face] = $face;
}
}
return $faces;
}
}

View File

@ -148,6 +148,7 @@ class Leaves extends Transparent{
LeavesType::SPRUCE() => VanillaBlocks::SPRUCE_SAPLING(), LeavesType::SPRUCE() => VanillaBlocks::SPRUCE_SAPLING(),
LeavesType::MANGROVE(), //TODO: mangrove propagule LeavesType::MANGROVE(), //TODO: mangrove propagule
LeavesType::AZALEA(), LeavesType::FLOWERING_AZALEA() => null, //TODO: azalea LeavesType::AZALEA(), LeavesType::FLOWERING_AZALEA() => null, //TODO: azalea
LeavesType::CHERRY() => null, //TODO: cherry
default => throw new AssumptionFailedError("Unreachable") default => throw new AssumptionFailedError("Unreachable")
})?->asItem(); })?->asItem();
if($sapling !== null){ if($sapling !== null){

View File

@ -159,6 +159,20 @@ use function mb_strtolower;
* @method static CaveVines CAVE_VINES() * @method static CaveVines CAVE_VINES()
* @method static Chain CHAIN() * @method static Chain CHAIN()
* @method static ChemicalHeat CHEMICAL_HEAT() * @method static ChemicalHeat CHEMICAL_HEAT()
* @method static WoodenButton CHERRY_BUTTON()
* @method static WoodenDoor CHERRY_DOOR()
* @method static WoodenFence CHERRY_FENCE()
* @method static FenceGate CHERRY_FENCE_GATE()
* @method static Leaves CHERRY_LEAVES()
* @method static Wood CHERRY_LOG()
* @method static Planks CHERRY_PLANKS()
* @method static WoodenPressurePlate CHERRY_PRESSURE_PLATE()
* @method static FloorSign CHERRY_SIGN()
* @method static WoodenSlab CHERRY_SLAB()
* @method static WoodenStairs CHERRY_STAIRS()
* @method static WoodenTrapdoor CHERRY_TRAPDOOR()
* @method static WallSign CHERRY_WALL_SIGN()
* @method static Wood CHERRY_WOOD()
* @method static Chest CHEST() * @method static Chest CHEST()
* @method static Opaque CHISELED_DEEPSLATE() * @method static Opaque CHISELED_DEEPSLATE()
* @method static Opaque CHISELED_NETHER_BRICKS() * @method static Opaque CHISELED_NETHER_BRICKS()
@ -417,6 +431,7 @@ use function mb_strtolower;
* @method static ItemFrame GLOWING_ITEM_FRAME() * @method static ItemFrame GLOWING_ITEM_FRAME()
* @method static GlowingObsidian GLOWING_OBSIDIAN() * @method static GlowingObsidian GLOWING_OBSIDIAN()
* @method static Glowstone GLOWSTONE() * @method static Glowstone GLOWSTONE()
* @method static GlowLichen GLOW_LICHEN()
* @method static Opaque GOLD() * @method static Opaque GOLD()
* @method static GoldOre GOLD_ORE() * @method static GoldOre GOLD_ORE()
* @method static Opaque GRANITE() * @method static Opaque GRANITE()
@ -864,6 +879,7 @@ final class VanillaBlocks{
self::register("glass_pane", new GlassPane(new BID(Ids::GLASS_PANE), "Glass Pane", $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("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); $grassBreakInfo = BreakInfo::shovel(0.6);

View File

@ -58,6 +58,7 @@ final class WoodLikeBlockIdHelper{
WoodType::MANGROVE()->id() => Ids::MANGROVE_PLANKS, WoodType::MANGROVE()->id() => Ids::MANGROVE_PLANKS,
WoodType::CRIMSON()->id() => Ids::CRIMSON_PLANKS, WoodType::CRIMSON()->id() => Ids::CRIMSON_PLANKS,
WoodType::WARPED()->id() => Ids::WARPED_PLANKS, WoodType::WARPED()->id() => Ids::WARPED_PLANKS,
WoodType::CHERRY()->id() => Ids::CHERRY_PLANKS,
default => throw new AssumptionFailedError("All tree types should be covered") default => throw new AssumptionFailedError("All tree types should be covered")
}); });
} }
@ -73,6 +74,7 @@ final class WoodLikeBlockIdHelper{
WoodType::MANGROVE()->id() => Ids::MANGROVE_FENCE, WoodType::MANGROVE()->id() => Ids::MANGROVE_FENCE,
WoodType::CRIMSON()->id() => Ids::CRIMSON_FENCE, WoodType::CRIMSON()->id() => Ids::CRIMSON_FENCE,
WoodType::WARPED()->id() => Ids::WARPED_FENCE, WoodType::WARPED()->id() => Ids::WARPED_FENCE,
WoodType::CHERRY()->id() => Ids::CHERRY_FENCE,
default => throw new AssumptionFailedError("All tree types should be covered") default => throw new AssumptionFailedError("All tree types should be covered")
}); });
} }
@ -88,6 +90,7 @@ final class WoodLikeBlockIdHelper{
WoodType::MANGROVE()->id() => Ids::MANGROVE_SLAB, WoodType::MANGROVE()->id() => Ids::MANGROVE_SLAB,
WoodType::CRIMSON()->id() => Ids::CRIMSON_SLAB, WoodType::CRIMSON()->id() => Ids::CRIMSON_SLAB,
WoodType::WARPED()->id() => Ids::WARPED_SLAB, WoodType::WARPED()->id() => Ids::WARPED_SLAB,
WoodType::CHERRY()->id() => Ids::CHERRY_SLAB,
default => throw new AssumptionFailedError("All tree types should be covered") default => throw new AssumptionFailedError("All tree types should be covered")
}); });
} }
@ -103,6 +106,7 @@ final class WoodLikeBlockIdHelper{
WoodType::MANGROVE()->id() => Ids::MANGROVE_LOG, WoodType::MANGROVE()->id() => Ids::MANGROVE_LOG,
WoodType::CRIMSON()->id() => Ids::CRIMSON_STEM, WoodType::CRIMSON()->id() => Ids::CRIMSON_STEM,
WoodType::WARPED()->id() => Ids::WARPED_STEM, WoodType::WARPED()->id() => Ids::WARPED_STEM,
WoodType::CHERRY()->id() => Ids::CHERRY_LOG,
default => throw new AssumptionFailedError("All tree types should be covered") default => throw new AssumptionFailedError("All tree types should be covered")
}); });
} }
@ -118,6 +122,7 @@ final class WoodLikeBlockIdHelper{
WoodType::MANGROVE()->id() => Ids::MANGROVE_WOOD, WoodType::MANGROVE()->id() => Ids::MANGROVE_WOOD,
WoodType::CRIMSON()->id() => Ids::CRIMSON_HYPHAE, WoodType::CRIMSON()->id() => Ids::CRIMSON_HYPHAE,
WoodType::WARPED()->id() => Ids::WARPED_HYPHAE, WoodType::WARPED()->id() => Ids::WARPED_HYPHAE,
WoodType::CHERRY()->id() => Ids::CHERRY_WOOD,
default => throw new AssumptionFailedError("All tree types should be covered") default => throw new AssumptionFailedError("All tree types should be covered")
}); });
} }
@ -133,6 +138,7 @@ final class WoodLikeBlockIdHelper{
LeavesType::MANGROVE()->id() => Ids::MANGROVE_LEAVES, LeavesType::MANGROVE()->id() => Ids::MANGROVE_LEAVES,
LeavesType::AZALEA()->id() => Ids::AZALEA_LEAVES, LeavesType::AZALEA()->id() => Ids::AZALEA_LEAVES,
LeavesType::FLOWERING_AZALEA()->id() => Ids::FLOWERING_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") default => throw new AssumptionFailedError("All leaves types should be covered")
}); });
} }
@ -209,7 +215,12 @@ final class WoodLikeBlockIdHelper{
new BID(Ids::WARPED_WALL_SIGN, TileSign::class), new BID(Ids::WARPED_WALL_SIGN, TileSign::class),
fn() => VanillaItems::WARPED_SIGN() 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"); throw new AssumptionFailedError("Switch should cover all wood types");
} }
@ -225,6 +236,7 @@ final class WoodLikeBlockIdHelper{
WoodType::MANGROVE()->id() => Ids::MANGROVE_TRAPDOOR, WoodType::MANGROVE()->id() => Ids::MANGROVE_TRAPDOOR,
WoodType::CRIMSON()->id() => Ids::CRIMSON_TRAPDOOR, WoodType::CRIMSON()->id() => Ids::CRIMSON_TRAPDOOR,
WoodType::WARPED()->id() => Ids::WARPED_TRAPDOOR, WoodType::WARPED()->id() => Ids::WARPED_TRAPDOOR,
WoodType::CHERRY()->id() => Ids::CHERRY_TRAPDOOR,
default => throw new AssumptionFailedError("All wood types should be covered") default => throw new AssumptionFailedError("All wood types should be covered")
}); });
} }
@ -240,6 +252,7 @@ final class WoodLikeBlockIdHelper{
WoodType::MANGROVE()->id() => Ids::MANGROVE_BUTTON, WoodType::MANGROVE()->id() => Ids::MANGROVE_BUTTON,
WoodType::CRIMSON()->id() => Ids::CRIMSON_BUTTON, WoodType::CRIMSON()->id() => Ids::CRIMSON_BUTTON,
WoodType::WARPED()->id() => Ids::WARPED_BUTTON, WoodType::WARPED()->id() => Ids::WARPED_BUTTON,
WoodType::CHERRY()->id() => Ids::CHERRY_BUTTON,
default => throw new AssumptionFailedError("All wood types should be covered") default => throw new AssumptionFailedError("All wood types should be covered")
}); });
} }
@ -255,6 +268,7 @@ final class WoodLikeBlockIdHelper{
WoodType::MANGROVE()->id() => Ids::MANGROVE_PRESSURE_PLATE, WoodType::MANGROVE()->id() => Ids::MANGROVE_PRESSURE_PLATE,
WoodType::CRIMSON()->id() => Ids::CRIMSON_PRESSURE_PLATE, WoodType::CRIMSON()->id() => Ids::CRIMSON_PRESSURE_PLATE,
WoodType::WARPED()->id() => Ids::WARPED_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") default => throw new AssumptionFailedError("All wood types should be covered")
}); });
} }
@ -270,6 +284,7 @@ final class WoodLikeBlockIdHelper{
WoodType::MANGROVE()->id() => Ids::MANGROVE_DOOR, WoodType::MANGROVE()->id() => Ids::MANGROVE_DOOR,
WoodType::CRIMSON()->id() => Ids::CRIMSON_DOOR, WoodType::CRIMSON()->id() => Ids::CRIMSON_DOOR,
WoodType::WARPED()->id() => Ids::WARPED_DOOR, WoodType::WARPED()->id() => Ids::WARPED_DOOR,
WoodType::CHERRY()->id() => Ids::CHERRY_DOOR,
default => throw new AssumptionFailedError("All wood types should be covered") default => throw new AssumptionFailedError("All wood types should be covered")
}); });
} }
@ -285,6 +300,7 @@ final class WoodLikeBlockIdHelper{
WoodType::MANGROVE()->id() => Ids::MANGROVE_FENCE_GATE, WoodType::MANGROVE()->id() => Ids::MANGROVE_FENCE_GATE,
WoodType::CRIMSON()->id() => Ids::CRIMSON_FENCE_GATE, WoodType::CRIMSON()->id() => Ids::CRIMSON_FENCE_GATE,
WoodType::WARPED()->id() => Ids::WARPED_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") default => throw new AssumptionFailedError("All wood types should be covered")
}); });
} }
@ -300,6 +316,7 @@ final class WoodLikeBlockIdHelper{
WoodType::MANGROVE()->id() => Ids::MANGROVE_STAIRS, WoodType::MANGROVE()->id() => Ids::MANGROVE_STAIRS,
WoodType::CRIMSON()->id() => Ids::CRIMSON_STAIRS, WoodType::CRIMSON()->id() => Ids::CRIMSON_STAIRS,
WoodType::WARPED()->id() => Ids::WARPED_STAIRS, WoodType::WARPED()->id() => Ids::WARPED_STAIRS,
WoodType::CHERRY()->id() => Ids::CHERRY_STAIRS,
default => throw new AssumptionFailedError("All wood types should be covered") default => throw new AssumptionFailedError("All wood types should be covered")
}); });
} }

View File

@ -34,6 +34,7 @@ use pocketmine\utils\EnumTrait;
* @method static LeavesType ACACIA() * @method static LeavesType ACACIA()
* @method static LeavesType AZALEA() * @method static LeavesType AZALEA()
* @method static LeavesType BIRCH() * @method static LeavesType BIRCH()
* @method static LeavesType CHERRY()
* @method static LeavesType DARK_OAK() * @method static LeavesType DARK_OAK()
* @method static LeavesType FLOWERING_AZALEA() * @method static LeavesType FLOWERING_AZALEA()
* @method static LeavesType JUNGLE() * @method static LeavesType JUNGLE()
@ -57,7 +58,8 @@ final class LeavesType{
new self("dark_oak", "Dark Oak"), new self("dark_oak", "Dark Oak"),
new self("mangrove", "Mangrove"), new self("mangrove", "Mangrove"),
new self("azalea", "Azalea"), new self("azalea", "Azalea"),
new self("flowering_azalea", "Flowering Azalea") new self("flowering_azalea", "Flowering Azalea"),
new self("cherry", "Cherry")
); );
} }

View File

@ -33,6 +33,7 @@ use pocketmine\utils\EnumTrait;
* *
* @method static MobHeadType CREEPER() * @method static MobHeadType CREEPER()
* @method static MobHeadType DRAGON() * @method static MobHeadType DRAGON()
* @method static MobHeadType PIGLIN()
* @method static MobHeadType PLAYER() * @method static MobHeadType PLAYER()
* @method static MobHeadType SKELETON() * @method static MobHeadType SKELETON()
* @method static MobHeadType WITHER_SKELETON() * @method static MobHeadType WITHER_SKELETON()
@ -50,7 +51,8 @@ final class MobHeadType{
new MobHeadType("zombie", "Zombie Head"), new MobHeadType("zombie", "Zombie Head"),
new MobHeadType("player", "Player Head"), new MobHeadType("player", "Player Head"),
new MobHeadType("creeper", "Creeper Head"), new MobHeadType("creeper", "Creeper Head"),
new MobHeadType("dragon", "Dragon Head") new MobHeadType("dragon", "Dragon Head"),
new MobHeadType("piglin", "Piglin Head")
); );
} }

View File

@ -33,6 +33,7 @@ use pocketmine\utils\EnumTrait;
* *
* @method static WoodType ACACIA() * @method static WoodType ACACIA()
* @method static WoodType BIRCH() * @method static WoodType BIRCH()
* @method static WoodType CHERRY()
* @method static WoodType CRIMSON() * @method static WoodType CRIMSON()
* @method static WoodType DARK_OAK() * @method static WoodType DARK_OAK()
* @method static WoodType JUNGLE() * @method static WoodType JUNGLE()
@ -56,7 +57,8 @@ final class WoodType{
new self("dark_oak", "Dark Oak", true), new self("dark_oak", "Dark Oak", true),
new self("mangrove", "Mangrove", true), new self("mangrove", "Mangrove", true),
new self("crimson", "Crimson", false, "Stem", "Hyphae"), new self("crimson", "Crimson", false, "Stem", "Hyphae"),
new self("warped", "Warped", false, "Stem", "Hyphae") new self("warped", "Warped", false, "Stem", "Hyphae"),
new self("cherry", "Cherry", true),
); );
} }

View File

@ -48,6 +48,7 @@ final class MobHeadTypeIdMap{
$this->register(3, MobHeadType::PLAYER()); $this->register(3, MobHeadType::PLAYER());
$this->register(4, MobHeadType::CREEPER()); $this->register(4, MobHeadType::CREEPER());
$this->register(5, MobHeadType::DRAGON()); $this->register(5, MobHeadType::DRAGON());
$this->register(6, MobHeadType::PIGLIN());
} }
private function register(int $id, MobHeadType $type) : void{ private function register(int $id, MobHeadType $type) : void{

View File

@ -38,6 +38,13 @@ final class BlockLegacyMetadata{
public const CORAL_VARIANT_FIRE = 3; public const CORAL_VARIANT_FIRE = 3;
public const CORAL_VARIANT_HORN = 4; public const CORAL_VARIANT_HORN = 4;
public const MULTI_FACE_DIRECTION_FLAG_DOWN = 0x01;
public const MULTI_FACE_DIRECTION_FLAG_UP = 0x02;
public const MULTI_FACE_DIRECTION_FLAG_SOUTH = 0x04;
public const MULTI_FACE_DIRECTION_FLAG_WEST = 0x08;
public const MULTI_FACE_DIRECTION_FLAG_NORTH = 0x10;
public const MULTI_FACE_DIRECTION_FLAG_EAST = 0x20;
public const MUSHROOM_BLOCK_ALL_PORES = 0; public const MUSHROOM_BLOCK_ALL_PORES = 0;
public const MUSHROOM_BLOCK_CAP_NORTHWEST_CORNER = 1; public const MUSHROOM_BLOCK_CAP_NORTHWEST_CORNER = 1;
public const MUSHROOM_BLOCK_CAP_NORTH_SIDE = 2; public const MUSHROOM_BLOCK_CAP_NORTH_SIDE = 2;

View File

@ -111,7 +111,10 @@ final class BlockStateData{
return new self($name, $states->getValue(), $version); return new self($name, $states->getValue(), $version);
} }
public function toNbt() : CompoundTag{ /**
* Encodes the blockstate as a TAG_Compound, exactly as it would be in vanilla Bedrock.
*/
public function toVanillaNbt() : CompoundTag{
$statesTag = CompoundTag::create(); $statesTag = CompoundTag::create();
foreach(Utils::stringifyKeys($this->states) as $key => $value){ foreach(Utils::stringifyKeys($this->states) as $key => $value){
$statesTag->setTag($key, $value); $statesTag->setTag($key, $value);
@ -119,7 +122,15 @@ final class BlockStateData{
return CompoundTag::create() return CompoundTag::create()
->setString(self::TAG_NAME, $this->name) ->setString(self::TAG_NAME, $this->name)
->setInt(self::TAG_VERSION, $this->version) ->setInt(self::TAG_VERSION, $this->version)
->setTag(self::TAG_STATES, $statesTag) ->setTag(self::TAG_STATES, $statesTag);
}
/**
* Encodes the blockstate as a TAG_Compound, but with extra PM-specific metadata, used for fixing bugs in old saved
* data. This should be used for anything saved to disk.
*/
public function toNbt() : CompoundTag{
return $this->toVanillaNbt()
->setLong(VersionInfo::TAG_WORLD_DATA_VERSION, VersionInfo::WORLD_DATA_VERSION); ->setLong(VersionInfo::TAG_WORLD_DATA_VERSION, VersionInfo::WORLD_DATA_VERSION);
} }

View File

@ -79,6 +79,7 @@ use pocketmine\block\Froglight;
use pocketmine\block\FrostedIce; use pocketmine\block\FrostedIce;
use pocketmine\block\Furnace; use pocketmine\block\Furnace;
use pocketmine\block\GlazedTerracotta; use pocketmine\block\GlazedTerracotta;
use pocketmine\block\GlowLichen;
use pocketmine\block\HayBale; use pocketmine\block\HayBale;
use pocketmine\block\Hopper; use pocketmine\block\Hopper;
use pocketmine\block\ItemFrame; use pocketmine\block\ItemFrame;
@ -192,7 +193,9 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->registerFlatColorBlockSerializers(); $this->registerFlatColorBlockSerializers();
$this->registerFlatCoralSerializers(); $this->registerFlatCoralSerializers();
$this->registerCauldronSerializers(); $this->registerCauldronSerializers();
$this->registerWoodBlockSerializers(); $this->registerFlatWoodBlockSerializers();
$this->registerLegacyWoodBlockSerializers();
$this->registerLeavesSerializers();
$this->registerSimpleSerializers(); $this->registerSimpleSerializers();
$this->registerSerializers(); $this->registerSerializers();
} }
@ -390,20 +393,206 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->map(Blocks::WATER_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, $b->getFillLevel())); $this->map(Blocks::WATER_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, $b->getFillLevel()));
} }
private function registerWoodBlockSerializers() : void{ private function registerFlatWoodBlockSerializers() : void{
$this->mapSimple(Blocks::ACACIA_FENCE(), Ids::ACACIA_FENCE); $this->map(Blocks::ACACIA_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::ACACIA_BUTTON)));
$this->mapSimple(Blocks::BIRCH_FENCE(), Ids::BIRCH_FENCE); $this->map(Blocks::ACACIA_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::ACACIA_DOOR)));
$this->mapSimple(Blocks::DARK_OAK_FENCE(), Ids::DARK_OAK_FENCE); $this->map(Blocks::ACACIA_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::ACACIA_FENCE_GATE)));
$this->mapSimple(Blocks::JUNGLE_FENCE(), Ids::JUNGLE_FENCE); $this->map(Blocks::ACACIA_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::ACACIA_PRESSURE_PLATE)));
$this->mapSimple(Blocks::OAK_FENCE(), Ids::OAK_FENCE); $this->map(Blocks::ACACIA_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::ACACIA_STANDING_SIGN)));
$this->mapSimple(Blocks::SPRUCE_FENCE(), Ids::SPRUCE_FENCE); $this->map(Blocks::ACACIA_STAIRS(), fn(WoodenStairs $block) => Helper::encodeStairs($block, new Writer(Ids::ACACIA_STAIRS)));
$this->map(Blocks::ACACIA_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::ACACIA_TRAPDOOR)));
$this->map(Blocks::ACACIA_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::ACACIA_WALL_SIGN)));
$this->mapLog(Blocks::ACACIA_LOG(), Ids::ACACIA_LOG, Ids::STRIPPED_ACACIA_LOG); $this->mapLog(Blocks::ACACIA_LOG(), Ids::ACACIA_LOG, Ids::STRIPPED_ACACIA_LOG);
$this->mapSimple(Blocks::ACACIA_FENCE(), Ids::ACACIA_FENCE);
//wood, planks and slabs still use the old way of storing wood type
$this->map(Blocks::BIRCH_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::BIRCH_BUTTON)));
$this->map(Blocks::BIRCH_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::BIRCH_DOOR)));
$this->map(Blocks::BIRCH_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::BIRCH_FENCE_GATE)));
$this->map(Blocks::BIRCH_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::BIRCH_PRESSURE_PLATE)));
$this->map(Blocks::BIRCH_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::BIRCH_STANDING_SIGN)));
$this->map(Blocks::BIRCH_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::BIRCH_TRAPDOOR)));
$this->map(Blocks::BIRCH_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::BIRCH_WALL_SIGN)));
$this->mapLog(Blocks::BIRCH_LOG(), Ids::BIRCH_LOG, Ids::STRIPPED_BIRCH_LOG); $this->mapLog(Blocks::BIRCH_LOG(), Ids::BIRCH_LOG, Ids::STRIPPED_BIRCH_LOG);
$this->mapSimple(Blocks::BIRCH_FENCE(), Ids::BIRCH_FENCE);
$this->mapStairs(Blocks::BIRCH_STAIRS(), Ids::BIRCH_STAIRS);
//wood, planks and slabs still use the old way of storing wood type
$this->map(Blocks::CHERRY_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::CHERRY_BUTTON)));
$this->map(Blocks::CHERRY_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::CHERRY_DOOR)));
$this->map(Blocks::CHERRY_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::CHERRY_FENCE_GATE)));
$this->map(Blocks::CHERRY_LOG(), fn(Wood $block) => Helper::encodeLog($block, Ids::CHERRY_LOG, Ids::STRIPPED_CHERRY_LOG));
$this->map(Blocks::CHERRY_PRESSURE_PLATE(), fn(SimplePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::CHERRY_PRESSURE_PLATE)));
$this->map(Blocks::CHERRY_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::CHERRY_STANDING_SIGN)));
$this->map(Blocks::CHERRY_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::CHERRY_TRAPDOOR)));
$this->map(Blocks::CHERRY_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::CHERRY_WALL_SIGN)));
$this->mapSimple(Blocks::CHERRY_FENCE(), Ids::CHERRY_FENCE);
$this->mapSimple(Blocks::CHERRY_PLANKS(), Ids::CHERRY_PLANKS);
$this->mapSlab(Blocks::CHERRY_SLAB(), Ids::CHERRY_SLAB, Ids::CHERRY_DOUBLE_SLAB);
$this->mapStairs(Blocks::CHERRY_STAIRS(), Ids::CHERRY_STAIRS);
$this->map(Blocks::CHERRY_WOOD(), function(Wood $block) : Writer{
//we can't use the standard method for this because cherry_wood has a useless property attached to it
if(!$block->isStripped()){
return Writer::create(Ids::CHERRY_WOOD)
->writePillarAxis($block->getAxis())
->writeBool(StateNames::STRIPPED_BIT, false); //this is useless, but it has to be written
}else{
return Writer::create(Ids::STRIPPED_CHERRY_WOOD)
->writePillarAxis($block->getAxis());
}
});
$this->map(Blocks::CRIMSON_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::CRIMSON_BUTTON)));
$this->map(Blocks::CRIMSON_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::CRIMSON_DOOR)));
$this->map(Blocks::CRIMSON_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::CRIMSON_FENCE_GATE)));
$this->map(Blocks::CRIMSON_HYPHAE(), fn(Wood $block) => Helper::encodeLog($block, Ids::CRIMSON_HYPHAE, Ids::STRIPPED_CRIMSON_HYPHAE));
$this->map(Blocks::CRIMSON_PRESSURE_PLATE(), fn(SimplePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::CRIMSON_PRESSURE_PLATE)));
$this->map(Blocks::CRIMSON_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::CRIMSON_STANDING_SIGN)));
$this->map(Blocks::CRIMSON_STEM(), fn(Wood $block) => Helper::encodeLog($block, Ids::CRIMSON_STEM, Ids::STRIPPED_CRIMSON_STEM));
$this->map(Blocks::CRIMSON_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::CRIMSON_TRAPDOOR)));
$this->map(Blocks::CRIMSON_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::CRIMSON_WALL_SIGN)));
$this->mapSimple(Blocks::CRIMSON_FENCE(), Ids::CRIMSON_FENCE);
$this->mapSimple(Blocks::CRIMSON_PLANKS(), Ids::CRIMSON_PLANKS);
$this->mapSlab(Blocks::CRIMSON_SLAB(), Ids::CRIMSON_SLAB, Ids::CRIMSON_DOUBLE_SLAB);
$this->mapStairs(Blocks::CRIMSON_STAIRS(), Ids::CRIMSON_STAIRS);
$this->map(Blocks::DARK_OAK_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::DARK_OAK_BUTTON)));
$this->map(Blocks::DARK_OAK_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::DARK_OAK_DOOR)));
$this->map(Blocks::DARK_OAK_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::DARK_OAK_FENCE_GATE)));
$this->map(Blocks::DARK_OAK_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::DARK_OAK_PRESSURE_PLATE)));
$this->map(Blocks::DARK_OAK_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::DARKOAK_STANDING_SIGN)));
$this->map(Blocks::DARK_OAK_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::DARK_OAK_TRAPDOOR)));
$this->map(Blocks::DARK_OAK_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::DARKOAK_WALL_SIGN)));
$this->mapLog(Blocks::DARK_OAK_LOG(), Ids::DARK_OAK_LOG, Ids::STRIPPED_DARK_OAK_LOG); $this->mapLog(Blocks::DARK_OAK_LOG(), Ids::DARK_OAK_LOG, Ids::STRIPPED_DARK_OAK_LOG);
$this->mapSimple(Blocks::DARK_OAK_FENCE(), Ids::DARK_OAK_FENCE);
$this->mapStairs(Blocks::DARK_OAK_STAIRS(), Ids::DARK_OAK_STAIRS);
//wood, planks and slabs still use the old way of storing wood type
$this->map(Blocks::JUNGLE_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::JUNGLE_BUTTON)));
$this->map(Blocks::JUNGLE_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::JUNGLE_DOOR)));
$this->map(Blocks::JUNGLE_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::JUNGLE_FENCE_GATE)));
$this->map(Blocks::JUNGLE_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::JUNGLE_PRESSURE_PLATE)));
$this->map(Blocks::JUNGLE_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::JUNGLE_STANDING_SIGN)));
$this->map(Blocks::JUNGLE_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::JUNGLE_TRAPDOOR)));
$this->map(Blocks::JUNGLE_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::JUNGLE_WALL_SIGN)));
$this->mapLog(Blocks::JUNGLE_LOG(), Ids::JUNGLE_LOG, Ids::STRIPPED_JUNGLE_LOG); $this->mapLog(Blocks::JUNGLE_LOG(), Ids::JUNGLE_LOG, Ids::STRIPPED_JUNGLE_LOG);
$this->mapSimple(Blocks::JUNGLE_FENCE(), Ids::JUNGLE_FENCE);
$this->mapStairs(Blocks::JUNGLE_STAIRS(), Ids::JUNGLE_STAIRS);
//wood, planks and slabs still use the old way of storing wood type
$this->map(Blocks::MANGROVE_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::MANGROVE_BUTTON)));
$this->map(Blocks::MANGROVE_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::MANGROVE_DOOR)));
$this->map(Blocks::MANGROVE_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::MANGROVE_FENCE_GATE)));
$this->map(Blocks::MANGROVE_LOG(), fn(Wood $block) => Helper::encodeLog($block, Ids::MANGROVE_LOG, Ids::STRIPPED_MANGROVE_LOG));
$this->map(Blocks::MANGROVE_PRESSURE_PLATE(), fn(SimplePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::MANGROVE_PRESSURE_PLATE)));
$this->map(Blocks::MANGROVE_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::MANGROVE_STANDING_SIGN)));
$this->map(Blocks::MANGROVE_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::MANGROVE_TRAPDOOR)));
$this->map(Blocks::MANGROVE_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::MANGROVE_WALL_SIGN)));
$this->mapSimple(Blocks::MANGROVE_FENCE(), Ids::MANGROVE_FENCE);
$this->mapSimple(Blocks::MANGROVE_PLANKS(), Ids::MANGROVE_PLANKS);
$this->mapSlab(Blocks::MANGROVE_SLAB(), Ids::MANGROVE_SLAB, Ids::MANGROVE_DOUBLE_SLAB);
$this->mapStairs(Blocks::MANGROVE_STAIRS(), Ids::MANGROVE_STAIRS);
$this->map(Blocks::MANGROVE_WOOD(), function(Wood $block) : Writer{
//we can't use the standard method for this because mangrove_wood has a useless property attached to it
if(!$block->isStripped()){
return Writer::create(Ids::MANGROVE_WOOD)
->writePillarAxis($block->getAxis())
->writeBool(StateNames::STRIPPED_BIT, false); //this is useless, but it has to be written
}else{
return Writer::create(Ids::STRIPPED_MANGROVE_WOOD)
->writePillarAxis($block->getAxis());
}
});
$this->map(Blocks::OAK_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::WOODEN_BUTTON)));
$this->map(Blocks::OAK_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::WOODEN_DOOR)));
$this->map(Blocks::OAK_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::FENCE_GATE)));
$this->map(Blocks::OAK_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::WOODEN_PRESSURE_PLATE)));
$this->map(Blocks::OAK_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::STANDING_SIGN)));
$this->map(Blocks::OAK_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::TRAPDOOR)));
$this->map(Blocks::OAK_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::WALL_SIGN)));
$this->mapLog(Blocks::OAK_LOG(), Ids::OAK_LOG, Ids::STRIPPED_OAK_LOG); $this->mapLog(Blocks::OAK_LOG(), Ids::OAK_LOG, Ids::STRIPPED_OAK_LOG);
$this->mapSimple(Blocks::OAK_FENCE(), Ids::OAK_FENCE);
$this->mapStairs(Blocks::OAK_STAIRS(), Ids::OAK_STAIRS);
//wood, planks and slabs still use the old way of storing wood type
$this->mapSimple(Blocks::SPRUCE_FENCE(), Ids::SPRUCE_FENCE);
$this->mapLog(Blocks::SPRUCE_LOG(), Ids::SPRUCE_LOG, Ids::STRIPPED_SPRUCE_LOG); $this->mapLog(Blocks::SPRUCE_LOG(), Ids::SPRUCE_LOG, Ids::STRIPPED_SPRUCE_LOG);
$this->map(Blocks::SPRUCE_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::SPRUCE_BUTTON)));
$this->map(Blocks::SPRUCE_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::SPRUCE_DOOR)));
$this->map(Blocks::SPRUCE_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::SPRUCE_FENCE_GATE)));
$this->map(Blocks::SPRUCE_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::SPRUCE_PRESSURE_PLATE)));
$this->map(Blocks::SPRUCE_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::SPRUCE_STANDING_SIGN)));
$this->mapStairs(Blocks::SPRUCE_STAIRS(), Ids::SPRUCE_STAIRS);
$this->map(Blocks::SPRUCE_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::SPRUCE_TRAPDOOR)));
$this->map(Blocks::SPRUCE_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::SPRUCE_WALL_SIGN)));
//wood, planks and slabs still use the old way of storing wood type
$this->map(Blocks::WARPED_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::WARPED_BUTTON)));
$this->map(Blocks::WARPED_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::WARPED_DOOR)));
$this->map(Blocks::WARPED_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::WARPED_FENCE_GATE)));
$this->map(Blocks::WARPED_HYPHAE(), fn(Wood $block) => Helper::encodeLog($block, Ids::WARPED_HYPHAE, Ids::STRIPPED_WARPED_HYPHAE));
$this->map(Blocks::WARPED_PRESSURE_PLATE(), fn(SimplePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::WARPED_PRESSURE_PLATE)));
$this->map(Blocks::WARPED_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::WARPED_STANDING_SIGN)));
$this->map(Blocks::WARPED_STEM(), fn(Wood $block) => Helper::encodeLog($block, Ids::WARPED_STEM, Ids::STRIPPED_WARPED_STEM));
$this->map(Blocks::WARPED_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::WARPED_TRAPDOOR)));
$this->map(Blocks::WARPED_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::WARPED_WALL_SIGN)));
$this->mapSimple(Blocks::WARPED_FENCE(), Ids::WARPED_FENCE);
$this->mapSimple(Blocks::WARPED_PLANKS(), Ids::WARPED_PLANKS);
$this->mapSlab(Blocks::WARPED_SLAB(), Ids::WARPED_SLAB, Ids::WARPED_DOUBLE_SLAB);
$this->mapStairs(Blocks::WARPED_STAIRS(), Ids::WARPED_STAIRS);
}
private function registerLegacyWoodBlockSerializers() : void{
foreach([
StringValues::WOOD_TYPE_ACACIA => Blocks::ACACIA_PLANKS(),
StringValues::WOOD_TYPE_BIRCH => Blocks::BIRCH_PLANKS(),
StringValues::WOOD_TYPE_DARK_OAK => Blocks::DARK_OAK_PLANKS(),
StringValues::WOOD_TYPE_JUNGLE => Blocks::JUNGLE_PLANKS(),
StringValues::WOOD_TYPE_OAK => Blocks::OAK_PLANKS(),
StringValues::WOOD_TYPE_SPRUCE => Blocks::SPRUCE_PLANKS(),
] as $woodType => $block){
$this->map($block, fn() => Writer::create(Ids::PLANKS)
->writeString(StateNames::WOOD_TYPE, $woodType));
}
foreach([
StringValues::WOOD_TYPE_ACACIA => Blocks::ACACIA_SLAB(),
StringValues::WOOD_TYPE_BIRCH => Blocks::BIRCH_SLAB(),
StringValues::WOOD_TYPE_DARK_OAK => Blocks::DARK_OAK_SLAB(),
StringValues::WOOD_TYPE_JUNGLE => Blocks::JUNGLE_SLAB(),
StringValues::WOOD_TYPE_OAK => Blocks::OAK_SLAB(),
StringValues::WOOD_TYPE_SPRUCE => Blocks::SPRUCE_SLAB(),
] as $woodType => $block){
$this->map($block, fn(Slab $block) => Helper::encodeWoodenSlab($block, $woodType));
}
foreach([
Blocks::ACACIA_WOOD(),
Blocks::BIRCH_WOOD(),
Blocks::DARK_OAK_WOOD(),
Blocks::JUNGLE_WOOD(),
Blocks::OAK_WOOD(),
Blocks::SPRUCE_WOOD(),
] as $block){
$this->map($block, fn(Wood $block) => Helper::encodeAllSidedLog($block));
}
}
private function registerLeavesSerializers() : void{
//flattened IDs
$this->map(Blocks::AZALEA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::AZALEA_LEAVES)));
$this->map(Blocks::CHERRY_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::CHERRY_LEAVES)));
$this->map(Blocks::FLOWERING_AZALEA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::AZALEA_LEAVES_FLOWERED)));
$this->map(Blocks::MANGROVE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::MANGROVE_LEAVES)));
//legacy mess
$this->map(Blocks::ACACIA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves2($block, StringValues::NEW_LEAF_TYPE_ACACIA));
$this->map(Blocks::BIRCH_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves1($block, StringValues::OLD_LEAF_TYPE_BIRCH));
$this->map(Blocks::DARK_OAK_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves2($block, StringValues::NEW_LEAF_TYPE_DARK_OAK));
$this->map(Blocks::JUNGLE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves1($block, StringValues::OLD_LEAF_TYPE_JUNGLE));
$this->map(Blocks::OAK_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves1($block, StringValues::OLD_LEAF_TYPE_OAK));
$this->map(Blocks::SPRUCE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves1($block, StringValues::OLD_LEAF_TYPE_SPRUCE));
} }
private function registerSimpleSerializers() : void{ private function registerSimpleSerializers() : void{
@ -436,8 +625,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->mapSimple(Blocks::CRACKED_NETHER_BRICKS(), Ids::CRACKED_NETHER_BRICKS); $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::CRACKED_POLISHED_BLACKSTONE_BRICKS(), Ids::CRACKED_POLISHED_BLACKSTONE_BRICKS);
$this->mapSimple(Blocks::CRAFTING_TABLE(), Ids::CRAFTING_TABLE); $this->mapSimple(Blocks::CRAFTING_TABLE(), Ids::CRAFTING_TABLE);
$this->mapSimple(Blocks::CRIMSON_FENCE(), Ids::CRIMSON_FENCE);
$this->mapSimple(Blocks::CRIMSON_PLANKS(), Ids::CRIMSON_PLANKS);
$this->mapSimple(Blocks::CRYING_OBSIDIAN(), Ids::CRYING_OBSIDIAN); $this->mapSimple(Blocks::CRYING_OBSIDIAN(), Ids::CRYING_OBSIDIAN);
$this->mapSimple(Blocks::DANDELION(), Ids::YELLOW_FLOWER); $this->mapSimple(Blocks::DANDELION(), Ids::YELLOW_FLOWER);
$this->mapSimple(Blocks::DEAD_BUSH(), Ids::DEADBUSH); $this->mapSimple(Blocks::DEAD_BUSH(), Ids::DEADBUSH);
@ -607,8 +794,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->mapSimple(Blocks::LEGACY_STONECUTTER(), Ids::STONECUTTER); $this->mapSimple(Blocks::LEGACY_STONECUTTER(), Ids::STONECUTTER);
$this->mapSimple(Blocks::LILY_PAD(), Ids::WATERLILY); $this->mapSimple(Blocks::LILY_PAD(), Ids::WATERLILY);
$this->mapSimple(Blocks::MAGMA(), Ids::MAGMA); $this->mapSimple(Blocks::MAGMA(), Ids::MAGMA);
$this->mapSimple(Blocks::MANGROVE_FENCE(), Ids::MANGROVE_FENCE);
$this->mapSimple(Blocks::MANGROVE_PLANKS(), Ids::MANGROVE_PLANKS);
$this->mapSimple(Blocks::MANGROVE_ROOTS(), Ids::MANGROVE_ROOTS); $this->mapSimple(Blocks::MANGROVE_ROOTS(), Ids::MANGROVE_ROOTS);
$this->mapSimple(Blocks::MELON(), Ids::MELON_BLOCK); $this->mapSimple(Blocks::MELON(), Ids::MELON_BLOCK);
$this->mapSimple(Blocks::MONSTER_SPAWNER(), Ids::MOB_SPAWNER); $this->mapSimple(Blocks::MONSTER_SPAWNER(), Ids::MOB_SPAWNER);
@ -655,27 +840,12 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->mapSimple(Blocks::SPORE_BLOSSOM(), Ids::SPORE_BLOSSOM); $this->mapSimple(Blocks::SPORE_BLOSSOM(), Ids::SPORE_BLOSSOM);
$this->mapSimple(Blocks::TINTED_GLASS(), Ids::TINTED_GLASS); $this->mapSimple(Blocks::TINTED_GLASS(), Ids::TINTED_GLASS);
$this->mapSimple(Blocks::TUFF(), Ids::TUFF); $this->mapSimple(Blocks::TUFF(), Ids::TUFF);
$this->mapSimple(Blocks::WARPED_FENCE(), Ids::WARPED_FENCE);
$this->mapSimple(Blocks::WARPED_PLANKS(), Ids::WARPED_PLANKS);
$this->mapSimple(Blocks::WARPED_WART_BLOCK(), Ids::WARPED_WART_BLOCK); $this->mapSimple(Blocks::WARPED_WART_BLOCK(), Ids::WARPED_WART_BLOCK);
$this->mapSimple(Blocks::WITHER_ROSE(), Ids::WITHER_ROSE); $this->mapSimple(Blocks::WITHER_ROSE(), Ids::WITHER_ROSE);
} }
private function registerSerializers() : void{ private function registerSerializers() : void{
$this->map(Blocks::ACACIA_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::ACACIA_BUTTON)));
$this->map(Blocks::ACACIA_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::ACACIA_DOOR)));
$this->map(Blocks::ACACIA_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::ACACIA_FENCE_GATE)));
$this->map(Blocks::ACACIA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves2($block, StringValues::NEW_LEAF_TYPE_ACACIA));
$this->map(Blocks::ACACIA_PLANKS(), fn() => Writer::create(Ids::PLANKS)
->writeString(StateNames::WOOD_TYPE, StringValues::WOOD_TYPE_ACACIA));
$this->map(Blocks::ACACIA_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::ACACIA_PRESSURE_PLATE)));
$this->map(Blocks::ACACIA_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_ACACIA)); $this->map(Blocks::ACACIA_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_ACACIA));
$this->map(Blocks::ACACIA_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::ACACIA_STANDING_SIGN)));
$this->map(Blocks::ACACIA_SLAB(), fn(Slab $block) => Helper::encodeWoodenSlab($block, StringValues::WOOD_TYPE_ACACIA));
$this->map(Blocks::ACACIA_STAIRS(), fn(WoodenStairs $block) => Helper::encodeStairs($block, new Writer(Ids::ACACIA_STAIRS)));
$this->map(Blocks::ACACIA_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::ACACIA_TRAPDOOR)));
$this->map(Blocks::ACACIA_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::ACACIA_WALL_SIGN)));
$this->map(Blocks::ACACIA_WOOD(), fn(Wood $block) => Helper::encodeAllSidedLog($block));
$this->map(Blocks::ACTIVATOR_RAIL(), function(ActivatorRail $block) : Writer{ $this->map(Blocks::ACTIVATOR_RAIL(), function(ActivatorRail $block) : Writer{
return Writer::create(Ids::ACTIVATOR_RAIL) return Writer::create(Ids::ACTIVATOR_RAIL)
->writeBool(StateNames::RAIL_DATA_BIT, $block->isPowered()) ->writeBool(StateNames::RAIL_DATA_BIT, $block->isPowered())
@ -698,7 +868,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
default => throw new BlockStateSerializeException("Invalid Anvil damage {$damage}"), default => throw new BlockStateSerializeException("Invalid Anvil damage {$damage}"),
}); });
}); });
$this->map(Blocks::AZALEA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::AZALEA_LEAVES)));
$this->map(Blocks::AZURE_BLUET(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_HOUSTONIA)); $this->map(Blocks::AZURE_BLUET(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_HOUSTONIA));
$this->map(Blocks::BAMBOO(), function(Bamboo $block) : Writer{ $this->map(Blocks::BAMBOO(), function(Bamboo $block) : Writer{
return Writer::create(Ids::BAMBOO) return Writer::create(Ids::BAMBOO)
@ -749,20 +918,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
->writeLegacyHorizontalFacing($block->getFacing()); ->writeLegacyHorizontalFacing($block->getFacing());
}); });
$this->map(Blocks::BIRCH_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::BIRCH_BUTTON)));
$this->map(Blocks::BIRCH_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::BIRCH_DOOR)));
$this->map(Blocks::BIRCH_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::BIRCH_FENCE_GATE)));
$this->map(Blocks::BIRCH_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves1($block, StringValues::OLD_LEAF_TYPE_BIRCH));
$this->map(Blocks::BIRCH_PLANKS(), fn() => Writer::create(Ids::PLANKS)
->writeString(StateNames::WOOD_TYPE, StringValues::WOOD_TYPE_BIRCH));
$this->map(Blocks::BIRCH_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::BIRCH_PRESSURE_PLATE)));
$this->map(Blocks::BIRCH_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_BIRCH)); $this->map(Blocks::BIRCH_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_BIRCH));
$this->map(Blocks::BIRCH_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::BIRCH_STANDING_SIGN)));
$this->map(Blocks::BIRCH_SLAB(), fn(Slab $block) => Helper::encodeWoodenSlab($block, StringValues::WOOD_TYPE_BIRCH));
$this->mapStairs(Blocks::BIRCH_STAIRS(), Ids::BIRCH_STAIRS);
$this->map(Blocks::BIRCH_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::BIRCH_TRAPDOOR)));
$this->map(Blocks::BIRCH_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::BIRCH_WALL_SIGN)));
$this->map(Blocks::BIRCH_WOOD(), fn(Wood $block) => Helper::encodeAllSidedLog($block));
$this->mapSlab(Blocks::BLACKSTONE_SLAB(), Ids::BLACKSTONE_SLAB, Ids::BLACKSTONE_DOUBLE_SLAB); $this->mapSlab(Blocks::BLACKSTONE_SLAB(), Ids::BLACKSTONE_SLAB, Ids::BLACKSTONE_DOUBLE_SLAB);
$this->mapStairs(Blocks::BLACKSTONE_STAIRS(), Ids::BLACKSTONE_STAIRS); $this->mapStairs(Blocks::BLACKSTONE_STAIRS(), Ids::BLACKSTONE_STAIRS);
$this->map(Blocks::BLACKSTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::BLACKSTONE_WALL))); $this->map(Blocks::BLACKSTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::BLACKSTONE_WALL)));
@ -934,35 +1090,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
}); });
$this->map(Blocks::CORNFLOWER(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_CORNFLOWER)); $this->map(Blocks::CORNFLOWER(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_CORNFLOWER));
$this->map(Blocks::CRACKED_STONE_BRICKS(), fn() => Helper::encodeStoneBricks(StringValues::STONE_BRICK_TYPE_CRACKED)); $this->map(Blocks::CRACKED_STONE_BRICKS(), fn() => Helper::encodeStoneBricks(StringValues::STONE_BRICK_TYPE_CRACKED));
$this->map(Blocks::CRIMSON_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::CRIMSON_BUTTON)));
$this->map(Blocks::CRIMSON_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::CRIMSON_DOOR)));
$this->map(Blocks::CRIMSON_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::CRIMSON_FENCE_GATE)));
$this->map(Blocks::CRIMSON_HYPHAE(), fn(Wood $block) => Helper::encodeLog($block, Ids::CRIMSON_HYPHAE, Ids::STRIPPED_CRIMSON_HYPHAE));
$this->map(Blocks::CRIMSON_PRESSURE_PLATE(), fn(SimplePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::CRIMSON_PRESSURE_PLATE)));
$this->map(Blocks::CRIMSON_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::CRIMSON_STANDING_SIGN)));
$this->mapSlab(Blocks::CRIMSON_SLAB(), Ids::CRIMSON_SLAB, Ids::CRIMSON_DOUBLE_SLAB);
$this->mapStairs(Blocks::CRIMSON_STAIRS(), Ids::CRIMSON_STAIRS);
$this->map(Blocks::CRIMSON_STEM(), fn(Wood $block) => Helper::encodeLog($block, Ids::CRIMSON_STEM, Ids::STRIPPED_CRIMSON_STEM));
$this->map(Blocks::CRIMSON_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::CRIMSON_TRAPDOOR)));
$this->map(Blocks::CRIMSON_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::CRIMSON_WALL_SIGN)));
$this->map(Blocks::CUT_RED_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::RED_SANDSTONE, StringValues::SAND_STONE_TYPE_CUT)); $this->map(Blocks::CUT_RED_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::RED_SANDSTONE, StringValues::SAND_STONE_TYPE_CUT));
$this->map(Blocks::CUT_RED_SANDSTONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab4($block, StringValues::STONE_SLAB_TYPE_4_CUT_RED_SANDSTONE)); $this->map(Blocks::CUT_RED_SANDSTONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab4($block, StringValues::STONE_SLAB_TYPE_4_CUT_RED_SANDSTONE));
$this->map(Blocks::CUT_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::SANDSTONE, StringValues::SAND_STONE_TYPE_CUT)); $this->map(Blocks::CUT_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::SANDSTONE, StringValues::SAND_STONE_TYPE_CUT));
$this->map(Blocks::CUT_SANDSTONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab4($block, StringValues::STONE_SLAB_TYPE_4_CUT_SANDSTONE)); $this->map(Blocks::CUT_SANDSTONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab4($block, StringValues::STONE_SLAB_TYPE_4_CUT_SANDSTONE));
$this->map(Blocks::DARK_OAK_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::DARK_OAK_BUTTON)));
$this->map(Blocks::DARK_OAK_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::DARK_OAK_DOOR)));
$this->map(Blocks::DARK_OAK_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::DARK_OAK_FENCE_GATE)));
$this->map(Blocks::DARK_OAK_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves2($block, StringValues::NEW_LEAF_TYPE_DARK_OAK));
$this->map(Blocks::DARK_OAK_PLANKS(), fn() => Writer::create(Ids::PLANKS)
->writeString(StateNames::WOOD_TYPE, StringValues::WOOD_TYPE_DARK_OAK));
$this->map(Blocks::DARK_OAK_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::DARK_OAK_PRESSURE_PLATE)));
$this->map(Blocks::DARK_OAK_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_DARK_OAK)); $this->map(Blocks::DARK_OAK_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_DARK_OAK));
$this->map(Blocks::DARK_OAK_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::DARKOAK_STANDING_SIGN)));
$this->map(Blocks::DARK_OAK_SLAB(), fn(Slab $block) => Helper::encodeWoodenSlab($block, StringValues::WOOD_TYPE_DARK_OAK));
$this->mapStairs(Blocks::DARK_OAK_STAIRS(), Ids::DARK_OAK_STAIRS);
$this->map(Blocks::DARK_OAK_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::DARK_OAK_TRAPDOOR)));
$this->map(Blocks::DARK_OAK_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::DARKOAK_WALL_SIGN)));
$this->map(Blocks::DARK_OAK_WOOD(), fn(Wood $block) => Helper::encodeAllSidedLog($block));
$this->map(Blocks::DARK_PRISMARINE(), fn() => Writer::create(Ids::PRISMARINE) $this->map(Blocks::DARK_PRISMARINE(), fn() => Writer::create(Ids::PRISMARINE)
->writeString(StateNames::PRISMARINE_BLOCK_TYPE, StringValues::PRISMARINE_BLOCK_TYPE_DARK)); ->writeString(StateNames::PRISMARINE_BLOCK_TYPE, StringValues::PRISMARINE_BLOCK_TYPE_DARK));
$this->map(Blocks::DARK_PRISMARINE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab2($block, StringValues::STONE_SLAB_TYPE_2_PRISMARINE_DARK)); $this->map(Blocks::DARK_PRISMARINE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab2($block, StringValues::STONE_SLAB_TYPE_2_PRISMARINE_DARK));
@ -1040,7 +1172,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
return Writer::create(Ids::FLOWER_POT) return Writer::create(Ids::FLOWER_POT)
->writeBool(StateNames::UPDATE_BIT, false); //to keep MCPE happy ->writeBool(StateNames::UPDATE_BIT, false); //to keep MCPE happy
}); });
$this->map(Blocks::FLOWERING_AZALEA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::AZALEA_LEAVES_FLOWERED)));
$this->map(Blocks::FROGLIGHT(), function(Froglight $block){ $this->map(Blocks::FROGLIGHT(), function(Froglight $block){
return Writer::create(match($block->getFroglightType()){ return Writer::create(match($block->getFroglightType()){
FroglightType::OCHRE() => Ids::OCHRE_FROGLIGHT, FroglightType::OCHRE() => Ids::OCHRE_FROGLIGHT,
@ -1055,6 +1186,10 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
->writeInt(StateNames::AGE, $block->getAge()); ->writeInt(StateNames::AGE, $block->getAge());
}); });
$this->map(Blocks::FURNACE(), fn(Furnace $block) => Helper::encodeFurnace($block, Ids::FURNACE, Ids::LIT_FURNACE)); $this->map(Blocks::FURNACE(), fn(Furnace $block) => Helper::encodeFurnace($block, Ids::FURNACE, Ids::LIT_FURNACE));
$this->map(Blocks::GLOW_LICHEN(), function(GlowLichen $block) : Writer{
return Writer::create(Ids::GLOW_LICHEN)
->writeFacingFlags($block->getFaces());
});
$this->map(Blocks::GLOWING_ITEM_FRAME(), fn(ItemFrame $block) => Helper::encodeItemFrame($block, Ids::GLOW_FRAME)); $this->map(Blocks::GLOWING_ITEM_FRAME(), fn(ItemFrame $block) => Helper::encodeItemFrame($block, Ids::GLOW_FRAME));
$this->map(Blocks::GRANITE(), fn() => Helper::encodeStone(StringValues::STONE_TYPE_GRANITE)); $this->map(Blocks::GRANITE(), fn() => Helper::encodeStone(StringValues::STONE_TYPE_GRANITE));
$this->map(Blocks::GRANITE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_GRANITE)); $this->map(Blocks::GRANITE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_GRANITE));
@ -1086,20 +1221,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
$this->map(Blocks::IRON_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::IRON_DOOR))); $this->map(Blocks::IRON_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::IRON_DOOR)));
$this->map(Blocks::IRON_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::IRON_TRAPDOOR))); $this->map(Blocks::IRON_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::IRON_TRAPDOOR)));
$this->map(Blocks::ITEM_FRAME(), fn(ItemFrame $block) => Helper::encodeItemFrame($block, Ids::FRAME)); $this->map(Blocks::ITEM_FRAME(), fn(ItemFrame $block) => Helper::encodeItemFrame($block, Ids::FRAME));
$this->map(Blocks::JUNGLE_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::JUNGLE_BUTTON)));
$this->map(Blocks::JUNGLE_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::JUNGLE_DOOR)));
$this->map(Blocks::JUNGLE_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::JUNGLE_FENCE_GATE)));
$this->map(Blocks::JUNGLE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves1($block, StringValues::OLD_LEAF_TYPE_JUNGLE));
$this->map(Blocks::JUNGLE_PLANKS(), fn() => Writer::create(Ids::PLANKS)
->writeString(StateNames::WOOD_TYPE, StringValues::WOOD_TYPE_JUNGLE));
$this->map(Blocks::JUNGLE_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::JUNGLE_PRESSURE_PLATE)));
$this->map(Blocks::JUNGLE_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_JUNGLE)); $this->map(Blocks::JUNGLE_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_JUNGLE));
$this->map(Blocks::JUNGLE_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::JUNGLE_STANDING_SIGN)));
$this->map(Blocks::JUNGLE_SLAB(), fn(Slab $block) => Helper::encodeWoodenSlab($block, StringValues::WOOD_TYPE_JUNGLE));
$this->mapStairs(Blocks::JUNGLE_STAIRS(), Ids::JUNGLE_STAIRS);
$this->map(Blocks::JUNGLE_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::JUNGLE_TRAPDOOR)));
$this->map(Blocks::JUNGLE_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::JUNGLE_WALL_SIGN)));
$this->map(Blocks::JUNGLE_WOOD(), fn(Wood $block) => Helper::encodeAllSidedLog($block));
$this->map(Blocks::LAB_TABLE(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, StringValues::CHEMISTRY_TABLE_TYPE_LAB_TABLE, new Writer(Ids::CHEMISTRY_TABLE))); $this->map(Blocks::LAB_TABLE(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, StringValues::CHEMISTRY_TABLE_TYPE_LAB_TABLE, new Writer(Ids::CHEMISTRY_TABLE)));
$this->map(Blocks::LADDER(), function(Ladder $block) : Writer{ $this->map(Blocks::LADDER(), function(Ladder $block) : Writer{
return Writer::create(Ids::LADDER) return Writer::create(Ids::LADDER)
@ -1149,28 +1271,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
return Writer::create(Ids::LOOM) return Writer::create(Ids::LOOM)
->writeLegacyHorizontalFacing($block->getFacing()); ->writeLegacyHorizontalFacing($block->getFacing());
}); });
$this->map(Blocks::MANGROVE_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::MANGROVE_BUTTON)));
$this->map(Blocks::MANGROVE_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::MANGROVE_DOOR)));
$this->map(Blocks::MANGROVE_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::MANGROVE_FENCE_GATE)));
$this->map(Blocks::MANGROVE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::MANGROVE_LEAVES)));
$this->map(Blocks::MANGROVE_LOG(), fn(Wood $block) => Helper::encodeLog($block, Ids::MANGROVE_LOG, Ids::STRIPPED_MANGROVE_LOG));
$this->map(Blocks::MANGROVE_PRESSURE_PLATE(), fn(SimplePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::MANGROVE_PRESSURE_PLATE)));
$this->map(Blocks::MANGROVE_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::MANGROVE_STANDING_SIGN)));
$this->mapSlab(Blocks::MANGROVE_SLAB(), Ids::MANGROVE_SLAB, Ids::MANGROVE_DOUBLE_SLAB);
$this->mapStairs(Blocks::MANGROVE_STAIRS(), Ids::MANGROVE_STAIRS);
$this->map(Blocks::MANGROVE_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::MANGROVE_TRAPDOOR)));
$this->map(Blocks::MANGROVE_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::MANGROVE_WALL_SIGN)));
$this->map(Blocks::MANGROVE_WOOD(), function(Wood $block) : Writer{
//we can't use the standard method for this because mangrove_wood has a useless property attached to it
if(!$block->isStripped()){
return Writer::create(Ids::MANGROVE_WOOD)
->writePillarAxis($block->getAxis())
->writeBool(StateNames::STRIPPED_BIT, false); //this is useless, but it has to be written
}else{
return Writer::create(Ids::STRIPPED_MANGROVE_WOOD)
->writePillarAxis($block->getAxis());
}
});
$this->map(Blocks::MATERIAL_REDUCER(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, StringValues::CHEMISTRY_TABLE_TYPE_MATERIAL_REDUCER, new Writer(Ids::CHEMISTRY_TABLE))); $this->map(Blocks::MATERIAL_REDUCER(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, StringValues::CHEMISTRY_TABLE_TYPE_MATERIAL_REDUCER, new Writer(Ids::CHEMISTRY_TABLE)));
$this->map(Blocks::MELON_STEM(), fn(MelonStem $block) => Helper::encodeStem($block, new Writer(Ids::MELON_STEM))); $this->map(Blocks::MELON_STEM(), fn(MelonStem $block) => Helper::encodeStem($block, new Writer(Ids::MELON_STEM)));
$this->map(Blocks::MOB_HEAD(), function(MobHead $block) : Writer{ $this->map(Blocks::MOB_HEAD(), function(MobHead $block) : Writer{
@ -1206,20 +1306,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
return Writer::create(Ids::NETHER_WART) return Writer::create(Ids::NETHER_WART)
->writeInt(StateNames::AGE, $block->getAge()); ->writeInt(StateNames::AGE, $block->getAge());
}); });
$this->map(Blocks::OAK_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::WOODEN_BUTTON)));
$this->map(Blocks::OAK_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::WOODEN_DOOR)));
$this->map(Blocks::OAK_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::FENCE_GATE)));
$this->map(Blocks::OAK_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves1($block, StringValues::OLD_LEAF_TYPE_OAK));
$this->map(Blocks::OAK_PLANKS(), fn() => Writer::create(Ids::PLANKS)
->writeString(StateNames::WOOD_TYPE, StringValues::WOOD_TYPE_OAK));
$this->map(Blocks::OAK_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::WOODEN_PRESSURE_PLATE)));
$this->map(Blocks::OAK_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_OAK)); $this->map(Blocks::OAK_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_OAK));
$this->map(Blocks::OAK_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::STANDING_SIGN)));
$this->map(Blocks::OAK_SLAB(), fn(Slab $block) => Helper::encodeWoodenSlab($block, StringValues::WOOD_TYPE_OAK));
$this->mapStairs(Blocks::OAK_STAIRS(), Ids::OAK_STAIRS);
$this->map(Blocks::OAK_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::TRAPDOOR)));
$this->map(Blocks::OAK_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::WALL_SIGN)));
$this->map(Blocks::OAK_WOOD(), fn(Wood $block) => Helper::encodeAllSidedLog($block));
$this->map(Blocks::ORANGE_TULIP(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_TULIP_ORANGE)); $this->map(Blocks::ORANGE_TULIP(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_TULIP_ORANGE));
$this->map(Blocks::OXEYE_DAISY(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_OXEYE)); $this->map(Blocks::OXEYE_DAISY(), fn() => Helper::encodeRedFlower(StringValues::FLOWER_TYPE_OXEYE));
$this->map(Blocks::PEONY(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, StringValues::DOUBLE_PLANT_TYPE_PAEONIA, Writer::create(Ids::DOUBLE_PLANT))); $this->map(Blocks::PEONY(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, StringValues::DOUBLE_PLANT_TYPE_PAEONIA, Writer::create(Ids::DOUBLE_PLANT)));
@ -1367,20 +1454,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
return Writer::create(Ids::SPONGE) return Writer::create(Ids::SPONGE)
->writeString(StateNames::SPONGE_TYPE, $block->isWet() ? StringValues::SPONGE_TYPE_WET : StringValues::SPONGE_TYPE_DRY); ->writeString(StateNames::SPONGE_TYPE, $block->isWet() ? StringValues::SPONGE_TYPE_WET : StringValues::SPONGE_TYPE_DRY);
}); });
$this->map(Blocks::SPRUCE_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::SPRUCE_BUTTON)));
$this->map(Blocks::SPRUCE_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::SPRUCE_DOOR)));
$this->map(Blocks::SPRUCE_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::SPRUCE_FENCE_GATE)));
$this->map(Blocks::SPRUCE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves1($block, StringValues::OLD_LEAF_TYPE_SPRUCE));
$this->map(Blocks::SPRUCE_PLANKS(), fn() => Writer::create(Ids::PLANKS)
->writeString(StateNames::WOOD_TYPE, StringValues::WOOD_TYPE_SPRUCE));
$this->map(Blocks::SPRUCE_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::SPRUCE_PRESSURE_PLATE)));
$this->map(Blocks::SPRUCE_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_SPRUCE)); $this->map(Blocks::SPRUCE_SAPLING(), fn(Sapling $block) => Helper::encodeSapling($block, StringValues::SAPLING_TYPE_SPRUCE));
$this->map(Blocks::SPRUCE_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::SPRUCE_STANDING_SIGN)));
$this->map(Blocks::SPRUCE_SLAB(), fn(Slab $block) => Helper::encodeWoodenSlab($block, StringValues::WOOD_TYPE_SPRUCE));
$this->mapStairs(Blocks::SPRUCE_STAIRS(), Ids::SPRUCE_STAIRS);
$this->map(Blocks::SPRUCE_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::SPRUCE_TRAPDOOR)));
$this->map(Blocks::SPRUCE_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::SPRUCE_WALL_SIGN)));
$this->map(Blocks::SPRUCE_WOOD(), fn(Wood $block) => Helper::encodeAllSidedLog($block));
$this->map(Blocks::STAINED_CLAY(), function(StainedHardenedClay $block) : Writer{ $this->map(Blocks::STAINED_CLAY(), function(StainedHardenedClay $block) : Writer{
return Writer::create(Ids::STAINED_HARDENED_CLAY) return Writer::create(Ids::STAINED_HARDENED_CLAY)
->writeColor($block->getColor()); ->writeColor($block->getColor());
@ -1477,17 +1551,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
->writeBool(StateNames::DEAD_BIT, $block->isDead()) ->writeBool(StateNames::DEAD_BIT, $block->isDead())
->writeCoralFacing($block->getFacing()); ->writeCoralFacing($block->getFacing());
}); });
$this->map(Blocks::WARPED_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::WARPED_BUTTON)));
$this->map(Blocks::WARPED_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::WARPED_DOOR)));
$this->map(Blocks::WARPED_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::WARPED_FENCE_GATE)));
$this->map(Blocks::WARPED_HYPHAE(), fn(Wood $block) => Helper::encodeLog($block, Ids::WARPED_HYPHAE, Ids::STRIPPED_WARPED_HYPHAE));
$this->map(Blocks::WARPED_PRESSURE_PLATE(), fn(SimplePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::WARPED_PRESSURE_PLATE)));
$this->map(Blocks::WARPED_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::WARPED_STANDING_SIGN)));
$this->mapSlab(Blocks::WARPED_SLAB(), Ids::WARPED_SLAB, Ids::WARPED_DOUBLE_SLAB);
$this->mapStairs(Blocks::WARPED_STAIRS(), Ids::WARPED_STAIRS);
$this->map(Blocks::WARPED_STEM(), fn(Wood $block) => Helper::encodeLog($block, Ids::WARPED_STEM, Ids::STRIPPED_WARPED_STEM));
$this->map(Blocks::WARPED_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::WARPED_TRAPDOOR)));
$this->map(Blocks::WARPED_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::WARPED_WALL_SIGN)));
$this->map(Blocks::WATER(), fn(Water $block) => Helper::encodeLiquid($block, Ids::WATER, Ids::FLOWING_WATER)); $this->map(Blocks::WATER(), fn(Water $block) => Helper::encodeLiquid($block, Ids::WATER, Ids::FLOWING_WATER));
$this->map(Blocks::WEEPING_VINES(), function(NetherVines $block) : Writer{ $this->map(Blocks::WEEPING_VINES(), function(NetherVines $block) : Writer{
return Writer::create(Ids::WEEPING_VINES) return Writer::create(Ids::WEEPING_VINES)

View File

@ -28,6 +28,7 @@ use pocketmine\block\utils\CoralType;
use pocketmine\block\utils\DyeColor; use pocketmine\block\utils\DyeColor;
use pocketmine\block\utils\SlabType; use pocketmine\block\utils\SlabType;
use pocketmine\block\utils\WallConnectionType; use pocketmine\block\utils\WallConnectionType;
use pocketmine\data\bedrock\block\BlockLegacyMetadata;
use pocketmine\data\bedrock\block\BlockStateData; use pocketmine\data\bedrock\block\BlockStateData;
use pocketmine\data\bedrock\block\BlockStateDeserializeException; use pocketmine\data\bedrock\block\BlockStateDeserializeException;
use pocketmine\data\bedrock\block\BlockStateNames; use pocketmine\data\bedrock\block\BlockStateNames;
@ -134,6 +135,29 @@ final class BlockStateReader{
]); ]);
} }
/**
* @return int[]
* @phpstan-return array<int, int>
*/
public function readFacingFlags() : array{
$result = [];
$flags = $this->readBoundedInt(BlockStateNames::MULTI_FACE_DIRECTION_BITS, 0, 63);
foreach([
BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_DOWN => Facing::DOWN,
BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_UP => Facing::UP,
BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_NORTH => Facing::NORTH,
BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_SOUTH => Facing::SOUTH,
BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_WEST => Facing::WEST,
BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_EAST => Facing::EAST
] as $flag => $facing){
if(($flags & $flag) !== 0){
$result[$facing] = $facing;
}
}
return $result;
}
/** @throws BlockStateDeserializeException */ /** @throws BlockStateDeserializeException */
public function readEndRodFacingDirection() : int{ public function readEndRodFacingDirection() : int{
$result = $this->readFacingDirection(); $result = $this->readFacingDirection();

View File

@ -75,7 +75,9 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->registerFlatColorBlockDeserializers(); $this->registerFlatColorBlockDeserializers();
$this->registerFlatCoralDeserializers(); $this->registerFlatCoralDeserializers();
$this->registerCauldronDeserializers(); $this->registerCauldronDeserializers();
$this->registerWoodBlockDeserializers(); $this->registerFlatWoodBlockDeserializers();
$this->registerLegacyWoodBlockDeserializers();
$this->registerLeavesDeserializers();
$this->registerSimpleDeserializers(); $this->registerSimpleDeserializers();
$this->registerDeserializers(); $this->registerDeserializers();
} }
@ -283,20 +285,191 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->map(Ids::CAULDRON, $deserializer); $this->map(Ids::CAULDRON, $deserializer);
} }
private function registerWoodBlockDeserializers() : void{ private function registerFlatWoodBlockDeserializers() : void{
$this->mapSimple(Ids::ACACIA_FENCE, fn() => Blocks::ACACIA_FENCE()); $this->map(Ids::ACACIA_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::ACACIA_BUTTON(), $in));
$this->mapSimple(Ids::BIRCH_FENCE, fn() => Blocks::BIRCH_FENCE()); $this->map(Ids::ACACIA_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::ACACIA_DOOR(), $in));
$this->mapSimple(Ids::DARK_OAK_FENCE, fn() => Blocks::DARK_OAK_FENCE()); $this->map(Ids::ACACIA_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::ACACIA_FENCE_GATE(), $in));
$this->mapSimple(Ids::JUNGLE_FENCE, fn() => Blocks::JUNGLE_FENCE()); $this->map(Ids::ACACIA_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::ACACIA_PRESSURE_PLATE(), $in));
$this->mapSimple(Ids::OAK_FENCE, fn() => Blocks::OAK_FENCE()); $this->map(Ids::ACACIA_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::ACACIA_SIGN(), $in));
$this->mapSimple(Ids::SPRUCE_FENCE, fn() => Blocks::SPRUCE_FENCE()); $this->map(Ids::ACACIA_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::ACACIA_TRAPDOOR(), $in));
$this->map(Ids::ACACIA_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::ACACIA_WALL_SIGN(), $in));
$this->mapLog(Ids::ACACIA_LOG, Ids::STRIPPED_ACACIA_LOG, fn() => Blocks::ACACIA_LOG()); $this->mapLog(Ids::ACACIA_LOG, Ids::STRIPPED_ACACIA_LOG, fn() => Blocks::ACACIA_LOG());
$this->mapSimple(Ids::ACACIA_FENCE, fn() => Blocks::ACACIA_FENCE());
$this->mapStairs(Ids::ACACIA_STAIRS, fn() => Blocks::ACACIA_STAIRS());
//wood, planks and slabs still use the old way of storing wood type
$this->map(Ids::BIRCH_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::BIRCH_BUTTON(), $in));
$this->map(Ids::BIRCH_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::BIRCH_DOOR(), $in));
$this->map(Ids::BIRCH_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::BIRCH_FENCE_GATE(), $in));
$this->map(Ids::BIRCH_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::BIRCH_PRESSURE_PLATE(), $in));
$this->map(Ids::BIRCH_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::BIRCH_SIGN(), $in));
$this->map(Ids::BIRCH_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::BIRCH_TRAPDOOR(), $in));
$this->map(Ids::BIRCH_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::BIRCH_WALL_SIGN(), $in));
$this->mapLog(Ids::BIRCH_LOG, Ids::STRIPPED_BIRCH_LOG, fn() => Blocks::BIRCH_LOG()); $this->mapLog(Ids::BIRCH_LOG, Ids::STRIPPED_BIRCH_LOG, fn() => Blocks::BIRCH_LOG());
$this->mapSimple(Ids::BIRCH_FENCE, fn() => Blocks::BIRCH_FENCE());
$this->mapStairs(Ids::BIRCH_STAIRS, fn() => Blocks::BIRCH_STAIRS());
//wood, planks and slabs still use the old way of storing wood type
$this->map(Ids::CHERRY_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::CHERRY_BUTTON(), $in));
$this->map(Ids::CHERRY_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::CHERRY_DOOR(), $in));
$this->map(Ids::CHERRY_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::CHERRY_FENCE_GATE(), $in));
$this->map(Ids::CHERRY_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::CHERRY_PRESSURE_PLATE(), $in));
$this->map(Ids::CHERRY_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::CHERRY_SIGN(), $in));
$this->map(Ids::CHERRY_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::CHERRY_TRAPDOOR(), $in));
$this->map(Ids::CHERRY_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::CHERRY_WALL_SIGN(), $in));
$this->mapLog(Ids::CHERRY_LOG, Ids::STRIPPED_CHERRY_LOG, fn() => Blocks::CHERRY_LOG());
$this->mapSimple(Ids::CHERRY_FENCE, fn() => Blocks::CHERRY_FENCE());
$this->mapSimple(Ids::CHERRY_PLANKS, fn() => Blocks::CHERRY_PLANKS());
$this->mapSlab(Ids::CHERRY_SLAB, Ids::CHERRY_DOUBLE_SLAB, fn() => Blocks::CHERRY_SLAB());
$this->mapStairs(Ids::CHERRY_STAIRS, fn() => Blocks::CHERRY_STAIRS());
$this->map(Ids::CHERRY_WOOD, function(Reader $in){
$in->ignored(StateNames::STRIPPED_BIT); //this is also ignored by vanilla
return Helper::decodeLog(Blocks::CHERRY_WOOD(), false, $in);
});
$this->map(Ids::STRIPPED_CHERRY_WOOD, fn(Reader $in) => Helper::decodeLog(Blocks::CHERRY_WOOD(), true, $in));
$this->map(Ids::CRIMSON_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::CRIMSON_BUTTON(), $in));
$this->map(Ids::CRIMSON_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::CRIMSON_DOOR(), $in));
$this->map(Ids::CRIMSON_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::CRIMSON_FENCE_GATE(), $in));
$this->map(Ids::CRIMSON_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::CRIMSON_PRESSURE_PLATE(), $in));
$this->map(Ids::CRIMSON_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::CRIMSON_SIGN(), $in));
$this->map(Ids::CRIMSON_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::CRIMSON_TRAPDOOR(), $in));
$this->map(Ids::CRIMSON_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::CRIMSON_WALL_SIGN(), $in));
$this->mapLog(Ids::CRIMSON_HYPHAE, Ids::STRIPPED_CRIMSON_HYPHAE, fn() => Blocks::CRIMSON_HYPHAE());
$this->mapLog(Ids::CRIMSON_STEM, Ids::STRIPPED_CRIMSON_STEM, fn() => Blocks::CRIMSON_STEM());
$this->mapSimple(Ids::CRIMSON_FENCE, fn() => Blocks::CRIMSON_FENCE());
$this->mapSimple(Ids::CRIMSON_PLANKS, fn() => Blocks::CRIMSON_PLANKS());
$this->mapSlab(Ids::CRIMSON_SLAB, Ids::CRIMSON_DOUBLE_SLAB, fn() => Blocks::CRIMSON_SLAB());
$this->mapStairs(Ids::CRIMSON_STAIRS, fn() => Blocks::CRIMSON_STAIRS());
$this->map(Ids::DARKOAK_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::DARK_OAK_SIGN(), $in));
$this->map(Ids::DARKOAK_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::DARK_OAK_WALL_SIGN(), $in));
$this->map(Ids::DARK_OAK_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::DARK_OAK_BUTTON(), $in));
$this->map(Ids::DARK_OAK_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::DARK_OAK_DOOR(), $in));
$this->map(Ids::DARK_OAK_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::DARK_OAK_FENCE_GATE(), $in));
$this->map(Ids::DARK_OAK_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::DARK_OAK_PRESSURE_PLATE(), $in));
$this->map(Ids::DARK_OAK_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::DARK_OAK_TRAPDOOR(), $in));
$this->mapLog(Ids::DARK_OAK_LOG, Ids::STRIPPED_DARK_OAK_LOG, fn() => Blocks::DARK_OAK_LOG()); $this->mapLog(Ids::DARK_OAK_LOG, Ids::STRIPPED_DARK_OAK_LOG, fn() => Blocks::DARK_OAK_LOG());
$this->mapSimple(Ids::DARK_OAK_FENCE, fn() => Blocks::DARK_OAK_FENCE());
$this->mapStairs(Ids::DARK_OAK_STAIRS, fn() => Blocks::DARK_OAK_STAIRS());
//wood, planks and slabs still use the old way of storing wood type
$this->map(Ids::JUNGLE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::JUNGLE_BUTTON(), $in));
$this->map(Ids::JUNGLE_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::JUNGLE_DOOR(), $in));
$this->map(Ids::JUNGLE_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::JUNGLE_FENCE_GATE(), $in));
$this->map(Ids::JUNGLE_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::JUNGLE_PRESSURE_PLATE(), $in));
$this->map(Ids::JUNGLE_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::JUNGLE_SIGN(), $in));
$this->map(Ids::JUNGLE_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::JUNGLE_TRAPDOOR(), $in));
$this->map(Ids::JUNGLE_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::JUNGLE_WALL_SIGN(), $in));
$this->mapLog(Ids::JUNGLE_LOG, Ids::STRIPPED_JUNGLE_LOG, fn() => Blocks::JUNGLE_LOG()); $this->mapLog(Ids::JUNGLE_LOG, Ids::STRIPPED_JUNGLE_LOG, fn() => Blocks::JUNGLE_LOG());
$this->mapSimple(Ids::JUNGLE_FENCE, fn() => Blocks::JUNGLE_FENCE());
$this->mapStairs(Ids::JUNGLE_STAIRS, fn() => Blocks::JUNGLE_STAIRS());
//wood, planks and slabs still use the old way of storing wood type
$this->map(Ids::MANGROVE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::MANGROVE_BUTTON(), $in));
$this->map(Ids::MANGROVE_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::MANGROVE_DOOR(), $in));
$this->map(Ids::MANGROVE_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::MANGROVE_FENCE_GATE(), $in));
$this->map(Ids::MANGROVE_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::MANGROVE_PRESSURE_PLATE(), $in));
$this->map(Ids::MANGROVE_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::MANGROVE_SIGN(), $in));
$this->map(Ids::MANGROVE_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::MANGROVE_TRAPDOOR(), $in));
$this->map(Ids::MANGROVE_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::MANGROVE_WALL_SIGN(), $in));
$this->mapLog(Ids::MANGROVE_LOG, Ids::STRIPPED_MANGROVE_LOG, fn() => Blocks::MANGROVE_LOG());
$this->mapSimple(Ids::MANGROVE_FENCE, fn() => Blocks::MANGROVE_FENCE());
$this->mapSimple(Ids::MANGROVE_PLANKS, fn() => Blocks::MANGROVE_PLANKS());
$this->mapSlab(Ids::MANGROVE_SLAB, Ids::MANGROVE_DOUBLE_SLAB, fn() => Blocks::MANGROVE_SLAB());
$this->mapStairs(Ids::MANGROVE_STAIRS, fn() => Blocks::MANGROVE_STAIRS());
$this->map(Ids::MANGROVE_WOOD, function(Reader $in){
$in->ignored(StateNames::STRIPPED_BIT); //this is also ignored by vanilla
return Helper::decodeLog(Blocks::MANGROVE_WOOD(), false, $in);
});
$this->map(Ids::STRIPPED_MANGROVE_WOOD, fn(Reader $in) => Helper::decodeLog(Blocks::MANGROVE_WOOD(), true, $in));
//oak - due to age, many of these don't specify "oak", making for confusing reading
$this->map(Ids::WOODEN_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::OAK_BUTTON(), $in));
$this->map(Ids::WOODEN_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::OAK_DOOR(), $in));
$this->map(Ids::FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::OAK_FENCE_GATE(), $in));
$this->map(Ids::WOODEN_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::OAK_PRESSURE_PLATE(), $in));
$this->map(Ids::STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::OAK_SIGN(), $in));
$this->map(Ids::TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::OAK_TRAPDOOR(), $in));
$this->map(Ids::WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::OAK_WALL_SIGN(), $in));
$this->mapLog(Ids::OAK_LOG, Ids::STRIPPED_OAK_LOG, fn() => Blocks::OAK_LOG()); $this->mapLog(Ids::OAK_LOG, Ids::STRIPPED_OAK_LOG, fn() => Blocks::OAK_LOG());
$this->mapSimple(Ids::OAK_FENCE, fn() => Blocks::OAK_FENCE());
$this->mapStairs(Ids::OAK_STAIRS, fn() => Blocks::OAK_STAIRS());
//wood, planks and slabs still use the old way of storing wood type
$this->map(Ids::SPRUCE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::SPRUCE_BUTTON(), $in));
$this->map(Ids::SPRUCE_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::SPRUCE_DOOR(), $in));
$this->map(Ids::SPRUCE_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::SPRUCE_FENCE_GATE(), $in));
$this->map(Ids::SPRUCE_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::SPRUCE_PRESSURE_PLATE(), $in));
$this->map(Ids::SPRUCE_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::SPRUCE_SIGN(), $in));
$this->map(Ids::SPRUCE_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::SPRUCE_TRAPDOOR(), $in));
$this->map(Ids::SPRUCE_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::SPRUCE_WALL_SIGN(), $in));
$this->mapLog(Ids::SPRUCE_LOG, Ids::STRIPPED_SPRUCE_LOG, fn() => Blocks::SPRUCE_LOG()); $this->mapLog(Ids::SPRUCE_LOG, Ids::STRIPPED_SPRUCE_LOG, fn() => Blocks::SPRUCE_LOG());
$this->mapSimple(Ids::SPRUCE_FENCE, fn() => Blocks::SPRUCE_FENCE());
$this->mapStairs(Ids::SPRUCE_STAIRS, fn() => Blocks::SPRUCE_STAIRS());
//wood, planks and slabs still use the old way of storing wood type
$this->map(Ids::WARPED_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::WARPED_BUTTON(), $in));
$this->map(Ids::WARPED_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::WARPED_DOOR(), $in));
$this->map(Ids::WARPED_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::WARPED_FENCE_GATE(), $in));
$this->map(Ids::WARPED_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::WARPED_PRESSURE_PLATE(), $in));
$this->map(Ids::WARPED_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::WARPED_SIGN(), $in));
$this->map(Ids::WARPED_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::WARPED_TRAPDOOR(), $in));
$this->map(Ids::WARPED_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::WARPED_WALL_SIGN(), $in));
$this->mapLog(Ids::WARPED_HYPHAE, Ids::STRIPPED_WARPED_HYPHAE, fn() => Blocks::WARPED_HYPHAE());
$this->mapLog(Ids::WARPED_STEM, Ids::STRIPPED_WARPED_STEM, fn() => Blocks::WARPED_STEM());
$this->mapSimple(Ids::WARPED_FENCE, fn() => Blocks::WARPED_FENCE());
$this->mapSimple(Ids::WARPED_PLANKS, fn() => Blocks::WARPED_PLANKS());
$this->mapSlab(Ids::WARPED_SLAB, Ids::WARPED_DOUBLE_SLAB, fn() => Blocks::WARPED_SLAB());
$this->mapStairs(Ids::WARPED_STAIRS, fn() => Blocks::WARPED_STAIRS());
}
private function registerLegacyWoodBlockDeserializers() : void{
$this->map(Ids::PLANKS, function(Reader $in) : Block{
return match($woodName = $in->readString(StateNames::WOOD_TYPE)){
StringValues::WOOD_TYPE_OAK => Blocks::OAK_PLANKS(),
StringValues::WOOD_TYPE_SPRUCE => Blocks::SPRUCE_PLANKS(),
StringValues::WOOD_TYPE_BIRCH => Blocks::BIRCH_PLANKS(),
StringValues::WOOD_TYPE_JUNGLE => Blocks::JUNGLE_PLANKS(),
StringValues::WOOD_TYPE_ACACIA => Blocks::ACACIA_PLANKS(),
StringValues::WOOD_TYPE_DARK_OAK => Blocks::DARK_OAK_PLANKS(),
default => throw $in->badValueException(StateNames::WOOD_TYPE, $woodName),
};
});
$this->mapSlab(Ids::WOODEN_SLAB, Ids::DOUBLE_WOODEN_SLAB, fn(Reader $in) => Helper::mapWoodenSlabType($in));
$this->map(Ids::WOOD, fn(Reader $in) : Block => Helper::decodeLog(match($woodType = $in->readString(StateNames::WOOD_TYPE)){
StringValues::WOOD_TYPE_ACACIA => Blocks::ACACIA_WOOD(),
StringValues::WOOD_TYPE_BIRCH => Blocks::BIRCH_WOOD(),
StringValues::WOOD_TYPE_DARK_OAK => Blocks::DARK_OAK_WOOD(),
StringValues::WOOD_TYPE_JUNGLE => Blocks::JUNGLE_WOOD(),
StringValues::WOOD_TYPE_OAK => Blocks::OAK_WOOD(),
StringValues::WOOD_TYPE_SPRUCE => Blocks::SPRUCE_WOOD(),
default => throw $in->badValueException(StateNames::WOOD_TYPE, $woodType),
}, $in->readBool(StateNames::STRIPPED_BIT), $in));
}
private function registerLeavesDeserializers() : void{
//flattened IDs
$this->map(Ids::AZALEA_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::AZALEA_LEAVES(), $in));
$this->map(Ids::AZALEA_LEAVES_FLOWERED, fn(Reader $in) => Helper::decodeLeaves(Blocks::FLOWERING_AZALEA_LEAVES(), $in));
$this->map(Ids::CHERRY_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::CHERRY_LEAVES(), $in));
$this->map(Ids::MANGROVE_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::MANGROVE_LEAVES(), $in));
//legacy mess
$this->map(Ids::LEAVES, fn(Reader $in) => Helper::decodeLeaves(match($type = $in->readString(StateNames::OLD_LEAF_TYPE)){
StringValues::OLD_LEAF_TYPE_BIRCH => Blocks::BIRCH_LEAVES(),
StringValues::OLD_LEAF_TYPE_JUNGLE => Blocks::JUNGLE_LEAVES(),
StringValues::OLD_LEAF_TYPE_OAK => Blocks::OAK_LEAVES(),
StringValues::OLD_LEAF_TYPE_SPRUCE => Blocks::SPRUCE_LEAVES(),
default => throw $in->badValueException(StateNames::OLD_LEAF_TYPE, $type),
}, $in));
$this->map(Ids::LEAVES2, fn(Reader $in) => Helper::decodeLeaves(match($type = $in->readString(StateNames::NEW_LEAF_TYPE)){
StringValues::NEW_LEAF_TYPE_ACACIA => Blocks::ACACIA_LEAVES(),
StringValues::NEW_LEAF_TYPE_DARK_OAK => Blocks::DARK_OAK_LEAVES(),
default => throw $in->badValueException(StateNames::NEW_LEAF_TYPE, $type),
}, $in));
} }
private function registerSimpleDeserializers() : void{ private function registerSimpleDeserializers() : void{
@ -328,8 +501,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->mapSimple(Ids::CRACKED_NETHER_BRICKS, fn() => Blocks::CRACKED_NETHER_BRICKS()); $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::CRACKED_POLISHED_BLACKSTONE_BRICKS, fn() => Blocks::CRACKED_POLISHED_BLACKSTONE_BRICKS());
$this->mapSimple(Ids::CRAFTING_TABLE, fn() => Blocks::CRAFTING_TABLE()); $this->mapSimple(Ids::CRAFTING_TABLE, fn() => Blocks::CRAFTING_TABLE());
$this->mapSimple(Ids::CRIMSON_FENCE, fn() => Blocks::CRIMSON_FENCE());
$this->mapSimple(Ids::CRIMSON_PLANKS, fn() => Blocks::CRIMSON_PLANKS());
$this->mapSimple(Ids::CRYING_OBSIDIAN, fn() => Blocks::CRYING_OBSIDIAN()); $this->mapSimple(Ids::CRYING_OBSIDIAN, fn() => Blocks::CRYING_OBSIDIAN());
$this->mapSimple(Ids::DEADBUSH, fn() => Blocks::DEAD_BUSH()); $this->mapSimple(Ids::DEADBUSH, fn() => Blocks::DEAD_BUSH());
$this->mapSimple(Ids::DEEPSLATE_BRICKS, fn() => Blocks::DEEPSLATE_BRICKS()); $this->mapSimple(Ids::DEEPSLATE_BRICKS, fn() => Blocks::DEEPSLATE_BRICKS());
@ -496,8 +667,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->mapSimple(Ids::LAPIS_BLOCK, fn() => Blocks::LAPIS_LAZULI()); $this->mapSimple(Ids::LAPIS_BLOCK, fn() => Blocks::LAPIS_LAZULI());
$this->mapSimple(Ids::LAPIS_ORE, fn() => Blocks::LAPIS_LAZULI_ORE()); $this->mapSimple(Ids::LAPIS_ORE, fn() => Blocks::LAPIS_LAZULI_ORE());
$this->mapSimple(Ids::MAGMA, fn() => Blocks::MAGMA()); $this->mapSimple(Ids::MAGMA, fn() => Blocks::MAGMA());
$this->mapSimple(Ids::MANGROVE_FENCE, fn() => Blocks::MANGROVE_FENCE());
$this->mapSimple(Ids::MANGROVE_PLANKS, fn() => Blocks::MANGROVE_PLANKS());
$this->mapSimple(Ids::MANGROVE_ROOTS, fn() => Blocks::MANGROVE_ROOTS()); $this->mapSimple(Ids::MANGROVE_ROOTS, fn() => Blocks::MANGROVE_ROOTS());
$this->mapSimple(Ids::MELON_BLOCK, fn() => Blocks::MELON()); $this->mapSimple(Ids::MELON_BLOCK, fn() => Blocks::MELON());
$this->mapSimple(Ids::MOB_SPAWNER, fn() => Blocks::MONSTER_SPAWNER()); $this->mapSimple(Ids::MOB_SPAWNER, fn() => Blocks::MONSTER_SPAWNER());
@ -545,8 +714,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
$this->mapSimple(Ids::TINTED_GLASS, fn() => Blocks::TINTED_GLASS()); $this->mapSimple(Ids::TINTED_GLASS, fn() => Blocks::TINTED_GLASS());
$this->mapSimple(Ids::TUFF, fn() => Blocks::TUFF()); $this->mapSimple(Ids::TUFF, fn() => Blocks::TUFF());
$this->mapSimple(Ids::UNDYED_SHULKER_BOX, fn() => Blocks::SHULKER_BOX()); $this->mapSimple(Ids::UNDYED_SHULKER_BOX, fn() => Blocks::SHULKER_BOX());
$this->mapSimple(Ids::WARPED_FENCE, fn() => Blocks::WARPED_FENCE());
$this->mapSimple(Ids::WARPED_PLANKS, fn() => Blocks::WARPED_PLANKS());
$this->mapSimple(Ids::WARPED_WART_BLOCK, fn() => Blocks::WARPED_WART_BLOCK()); $this->mapSimple(Ids::WARPED_WART_BLOCK, fn() => Blocks::WARPED_WART_BLOCK());
$this->mapSimple(Ids::WATERLILY, fn() => Blocks::LILY_PAD()); $this->mapSimple(Ids::WATERLILY, fn() => Blocks::LILY_PAD());
$this->mapSimple(Ids::WEB, fn() => Blocks::COBWEB()); $this->mapSimple(Ids::WEB, fn() => Blocks::COBWEB());
@ -555,14 +722,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
} }
private function registerDeserializers() : void{ private function registerDeserializers() : void{
$this->map(Ids::ACACIA_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::ACACIA_BUTTON(), $in));
$this->map(Ids::ACACIA_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::ACACIA_DOOR(), $in));
$this->map(Ids::ACACIA_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::ACACIA_FENCE_GATE(), $in));
$this->map(Ids::ACACIA_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::ACACIA_PRESSURE_PLATE(), $in));
$this->mapStairs(Ids::ACACIA_STAIRS, fn() => Blocks::ACACIA_STAIRS());
$this->map(Ids::ACACIA_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::ACACIA_SIGN(), $in));
$this->map(Ids::ACACIA_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::ACACIA_TRAPDOOR(), $in));
$this->map(Ids::ACACIA_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::ACACIA_WALL_SIGN(), $in));
$this->map(Ids::ACTIVATOR_RAIL, function(Reader $in) : Block{ $this->map(Ids::ACTIVATOR_RAIL, function(Reader $in) : Block{
return Blocks::ACTIVATOR_RAIL() return Blocks::ACTIVATOR_RAIL()
->setPowered($in->readBool(StateNames::RAIL_DATA_BIT)) ->setPowered($in->readBool(StateNames::RAIL_DATA_BIT))
@ -580,8 +739,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
}) })
->setFacing($in->readLegacyHorizontalFacing()); ->setFacing($in->readLegacyHorizontalFacing());
}); });
$this->map(Ids::AZALEA_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::AZALEA_LEAVES(), $in));
$this->map(Ids::AZALEA_LEAVES_FLOWERED, fn(Reader $in) => Helper::decodeLeaves(Blocks::FLOWERING_AZALEA_LEAVES(), $in));
$this->map(Ids::BAMBOO, function(Reader $in) : Block{ $this->map(Ids::BAMBOO, function(Reader $in) : Block{
return Blocks::BAMBOO() return Blocks::BAMBOO()
->setLeafSize(match($value = $in->readString(StateNames::BAMBOO_LEAF_SIZE)){ ->setLeafSize(match($value = $in->readString(StateNames::BAMBOO_LEAF_SIZE)){
@ -627,14 +784,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
->setFacing($in->readLegacyHorizontalFacing()) ->setFacing($in->readLegacyHorizontalFacing())
->setAttachmentType($in->readBellAttachmentType()); ->setAttachmentType($in->readBellAttachmentType());
}); });
$this->map(Ids::BIRCH_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::BIRCH_BUTTON(), $in));
$this->map(Ids::BIRCH_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::BIRCH_DOOR(), $in));
$this->map(Ids::BIRCH_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::BIRCH_FENCE_GATE(), $in));
$this->map(Ids::BIRCH_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::BIRCH_PRESSURE_PLATE(), $in));
$this->mapStairs(Ids::BIRCH_STAIRS, fn() => Blocks::BIRCH_STAIRS());
$this->map(Ids::BIRCH_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::BIRCH_SIGN(), $in));
$this->map(Ids::BIRCH_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::BIRCH_TRAPDOOR(), $in));
$this->map(Ids::BIRCH_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::BIRCH_WALL_SIGN(), $in));
$this->mapSlab(Ids::BLACKSTONE_SLAB, Ids::BLACKSTONE_DOUBLE_SLAB, fn() => Blocks::BLACKSTONE_SLAB()); $this->mapSlab(Ids::BLACKSTONE_SLAB, Ids::BLACKSTONE_DOUBLE_SLAB, fn() => Blocks::BLACKSTONE_SLAB());
$this->mapStairs(Ids::BLACKSTONE_STAIRS, fn() => Blocks::BLACKSTONE_STAIRS()); $this->mapStairs(Ids::BLACKSTONE_STAIRS, fn() => Blocks::BLACKSTONE_STAIRS());
$this->map(Ids::BLACKSTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::BLACKSTONE_WALL(), $in)); $this->map(Ids::BLACKSTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::BLACKSTONE_WALL(), $in));
@ -756,26 +905,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
return Helper::decodeWallCoralFan(Blocks::WALL_CORAL_FAN(), $in) return Helper::decodeWallCoralFan(Blocks::WALL_CORAL_FAN(), $in)
->setCoralType(CoralType::HORN()); ->setCoralType(CoralType::HORN());
}); });
$this->map(Ids::CRIMSON_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::CRIMSON_BUTTON(), $in));
$this->map(Ids::CRIMSON_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::CRIMSON_DOOR(), $in));
$this->mapSlab(Ids::CRIMSON_SLAB, Ids::CRIMSON_DOUBLE_SLAB, fn() => Blocks::CRIMSON_SLAB());
$this->map(Ids::CRIMSON_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::CRIMSON_FENCE_GATE(), $in));
$this->map(Ids::CRIMSON_HYPHAE, fn(Reader $in) => Helper::decodeLog(Blocks::CRIMSON_HYPHAE(), false, $in));
$this->map(Ids::CRIMSON_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::CRIMSON_PRESSURE_PLATE(), $in));
$this->mapStairs(Ids::CRIMSON_STAIRS, fn() => Blocks::CRIMSON_STAIRS());
$this->map(Ids::CRIMSON_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::CRIMSON_SIGN(), $in));
$this->map(Ids::CRIMSON_STEM, fn(Reader $in) => Helper::decodeLog(Blocks::CRIMSON_STEM(), false, $in));
$this->map(Ids::CRIMSON_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::CRIMSON_TRAPDOOR(), $in));
$this->map(Ids::CRIMSON_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::CRIMSON_WALL_SIGN(), $in));
$this->map(Ids::DARK_OAK_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::DARK_OAK_BUTTON(), $in));
$this->map(Ids::DARK_OAK_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::DARK_OAK_DOOR(), $in));
$this->map(Ids::DARK_OAK_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::DARK_OAK_FENCE_GATE(), $in));
$this->map(Ids::DARK_OAK_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::DARK_OAK_PRESSURE_PLATE(), $in));
$this->mapStairs(Ids::DARK_OAK_STAIRS, fn() => Blocks::DARK_OAK_STAIRS());
$this->map(Ids::DARK_OAK_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::DARK_OAK_TRAPDOOR(), $in));
$this->mapStairs(Ids::DARK_PRISMARINE_STAIRS, fn() => Blocks::DARK_PRISMARINE_STAIRS()); $this->mapStairs(Ids::DARK_PRISMARINE_STAIRS, fn() => Blocks::DARK_PRISMARINE_STAIRS());
$this->map(Ids::DARKOAK_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::DARK_OAK_SIGN(), $in));
$this->map(Ids::DARKOAK_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::DARK_OAK_WALL_SIGN(), $in));
$this->map(Ids::DAYLIGHT_DETECTOR, fn(Reader $in) => Helper::decodeDaylightSensor(Blocks::DAYLIGHT_SENSOR(), $in) $this->map(Ids::DAYLIGHT_DETECTOR, fn(Reader $in) => Helper::decodeDaylightSensor(Blocks::DAYLIGHT_SENSOR(), $in)
->setInverted(false)); ->setInverted(false));
$this->map(Ids::DAYLIGHT_DETECTOR_INVERTED, fn(Reader $in) => Helper::decodeDaylightSensor(Blocks::DAYLIGHT_SENSOR(), $in) $this->map(Ids::DAYLIGHT_DETECTOR_INVERTED, fn(Reader $in) => Helper::decodeDaylightSensor(Blocks::DAYLIGHT_SENSOR(), $in)
@ -839,7 +969,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
return Blocks::FARMLAND() return Blocks::FARMLAND()
->setWetness($in->readBoundedInt(StateNames::MOISTURIZED_AMOUNT, 0, 7)); ->setWetness($in->readBoundedInt(StateNames::MOISTURIZED_AMOUNT, 0, 7));
}); });
$this->map(Ids::FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::OAK_FENCE_GATE(), $in));
$this->map(Ids::FIRE, function(Reader $in) : Block{ $this->map(Ids::FIRE, function(Reader $in) : Block{
return Blocks::FIRE() return Blocks::FIRE()
->setAge($in->readBoundedInt(StateNames::AGE, 0, 15)); ->setAge($in->readBoundedInt(StateNames::AGE, 0, 15));
@ -860,6 +989,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
->setFacing($in->readHorizontalFacing()) ->setFacing($in->readHorizontalFacing())
->setLit(false); ->setLit(false);
}); });
$this->map(Ids::GLOW_LICHEN, fn(Reader $in) => Blocks::GLOW_LICHEN()->setFaces($in->readFacingFlags()));
$this->map(Ids::GLOW_FRAME, fn(Reader $in) => Helper::decodeItemFrame(Blocks::GLOWING_ITEM_FRAME(), $in)); $this->map(Ids::GLOW_FRAME, fn(Reader $in) => Helper::decodeItemFrame(Blocks::GLOWING_ITEM_FRAME(), $in));
$this->map(Ids::GOLDEN_RAIL, function(Reader $in) : Block{ $this->map(Ids::GOLDEN_RAIL, function(Reader $in) : Block{
return Blocks::POWERED_RAIL() return Blocks::POWERED_RAIL()
@ -887,14 +1017,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
}); });
$this->map(Ids::IRON_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::IRON_DOOR(), $in)); $this->map(Ids::IRON_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::IRON_DOOR(), $in));
$this->map(Ids::IRON_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::IRON_TRAPDOOR(), $in)); $this->map(Ids::IRON_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::IRON_TRAPDOOR(), $in));
$this->map(Ids::JUNGLE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::JUNGLE_BUTTON(), $in));
$this->map(Ids::JUNGLE_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::JUNGLE_DOOR(), $in));
$this->map(Ids::JUNGLE_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::JUNGLE_FENCE_GATE(), $in));
$this->map(Ids::JUNGLE_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::JUNGLE_PRESSURE_PLATE(), $in));
$this->mapStairs(Ids::JUNGLE_STAIRS, fn() => Blocks::JUNGLE_STAIRS());
$this->map(Ids::JUNGLE_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::JUNGLE_SIGN(), $in));
$this->map(Ids::JUNGLE_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::JUNGLE_TRAPDOOR(), $in));
$this->map(Ids::JUNGLE_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::JUNGLE_WALL_SIGN(), $in));
$this->map(Ids::LADDER, function(Reader $in) : Block{ $this->map(Ids::LADDER, function(Reader $in) : Block{
return Blocks::LADDER() return Blocks::LADDER()
->setFacing($in->readHorizontalFacing()); ->setFacing($in->readHorizontalFacing());
@ -904,18 +1026,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
->setHanging($in->readBool(StateNames::HANGING)); ->setHanging($in->readBool(StateNames::HANGING));
}); });
$this->map(Ids::LAVA, fn(Reader $in) => Helper::decodeStillLiquid(Blocks::LAVA(), $in)); $this->map(Ids::LAVA, fn(Reader $in) => Helper::decodeStillLiquid(Blocks::LAVA(), $in));
$this->map(Ids::LEAVES, fn(Reader $in) => Helper::decodeLeaves(match($type = $in->readString(StateNames::OLD_LEAF_TYPE)){
StringValues::OLD_LEAF_TYPE_BIRCH => Blocks::BIRCH_LEAVES(),
StringValues::OLD_LEAF_TYPE_JUNGLE => Blocks::JUNGLE_LEAVES(),
StringValues::OLD_LEAF_TYPE_OAK => Blocks::OAK_LEAVES(),
StringValues::OLD_LEAF_TYPE_SPRUCE => Blocks::SPRUCE_LEAVES(),
default => throw $in->badValueException(StateNames::OLD_LEAF_TYPE, $type),
}, $in));
$this->map(Ids::LEAVES2, fn(Reader $in) => Helper::decodeLeaves(match($type = $in->readString(StateNames::NEW_LEAF_TYPE)){
StringValues::NEW_LEAF_TYPE_ACACIA => Blocks::ACACIA_LEAVES(),
StringValues::NEW_LEAF_TYPE_DARK_OAK => Blocks::DARK_OAK_LEAVES(),
default => throw $in->badValueException(StateNames::NEW_LEAF_TYPE, $type),
}, $in));
$this->map(Ids::LECTERN, function(Reader $in) : Block{ $this->map(Ids::LECTERN, function(Reader $in) : Block{
return Blocks::LECTERN() return Blocks::LECTERN()
->setFacing($in->readLegacyHorizontalFacing()) ->setFacing($in->readLegacyHorizontalFacing())
@ -977,21 +1087,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
return Blocks::LOOM() return Blocks::LOOM()
->setFacing($in->readLegacyHorizontalFacing()); ->setFacing($in->readLegacyHorizontalFacing());
}); });
$this->map(Ids::MANGROVE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::MANGROVE_BUTTON(), $in));
$this->map(Ids::MANGROVE_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::MANGROVE_DOOR(), $in));
$this->mapSlab(Ids::MANGROVE_SLAB, Ids::MANGROVE_DOUBLE_SLAB, fn() => Blocks::MANGROVE_SLAB());
$this->map(Ids::MANGROVE_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::MANGROVE_FENCE_GATE(), $in));
$this->map(Ids::MANGROVE_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::MANGROVE_LEAVES(), $in));
$this->map(Ids::MANGROVE_LOG, fn(Reader $in) => Helper::decodeLog(Blocks::MANGROVE_LOG(), false, $in));
$this->map(Ids::MANGROVE_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::MANGROVE_PRESSURE_PLATE(), $in));
$this->mapStairs(Ids::MANGROVE_STAIRS, fn() => Blocks::MANGROVE_STAIRS());
$this->map(Ids::MANGROVE_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::MANGROVE_SIGN(), $in));
$this->map(Ids::MANGROVE_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::MANGROVE_TRAPDOOR(), $in));
$this->map(Ids::MANGROVE_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::MANGROVE_WALL_SIGN(), $in));
$this->map(Ids::MANGROVE_WOOD, function(Reader $in){
$in->ignored(StateNames::STRIPPED_BIT); //this is also ignored by vanilla
return Helper::decodeLog(Blocks::MANGROVE_WOOD(), false, $in);
});
$this->map(Ids::MELON_STEM, fn(Reader $in) => Helper::decodeStem(Blocks::MELON_STEM(), $in)); $this->map(Ids::MELON_STEM, fn(Reader $in) => Helper::decodeStem(Blocks::MELON_STEM(), $in));
$this->map(Ids::MONSTER_EGG, function(Reader $in) : Block{ $this->map(Ids::MONSTER_EGG, function(Reader $in) : Block{
return match($type = $in->readString(StateNames::MONSTER_EGG_STONE_TYPE)){ return match($type = $in->readString(StateNames::MONSTER_EGG_STONE_TYPE)){
@ -1019,24 +1114,12 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
->setAge($in->readBoundedInt(StateNames::AGE, 0, 3)); ->setAge($in->readBoundedInt(StateNames::AGE, 0, 3));
}); });
$this->mapStairs(Ids::NORMAL_STONE_STAIRS, fn() => Blocks::STONE_STAIRS()); $this->mapStairs(Ids::NORMAL_STONE_STAIRS, fn() => Blocks::STONE_STAIRS());
$this->mapStairs(Ids::OAK_STAIRS, fn() => Blocks::OAK_STAIRS());
$this->map(Ids::OCHRE_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::OCHRE())->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_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::OXIDIZED()));
$this->map(Ids::OXIDIZED_CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_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->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->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::PEARLESCENT_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::PEARLESCENT())->setAxis($in->readPillarAxis()));
$this->map(Ids::PLANKS, function(Reader $in) : Block{
return match($woodName = $in->readString(StateNames::WOOD_TYPE)){
StringValues::WOOD_TYPE_OAK => Blocks::OAK_PLANKS(),
StringValues::WOOD_TYPE_SPRUCE => Blocks::SPRUCE_PLANKS(),
StringValues::WOOD_TYPE_BIRCH => Blocks::BIRCH_PLANKS(),
StringValues::WOOD_TYPE_JUNGLE => Blocks::JUNGLE_PLANKS(),
StringValues::WOOD_TYPE_ACACIA => Blocks::ACACIA_PLANKS(),
StringValues::WOOD_TYPE_DARK_OAK => Blocks::DARK_OAK_PLANKS(),
default => throw $in->badValueException(StateNames::WOOD_TYPE, $woodName),
};
});
$this->mapStairs(Ids::POLISHED_ANDESITE_STAIRS, fn() => Blocks::POLISHED_ANDESITE_STAIRS()); $this->mapStairs(Ids::POLISHED_ANDESITE_STAIRS, fn() => Blocks::POLISHED_ANDESITE_STAIRS());
$this->map(Ids::POLISHED_BASALT, function(Reader $in) : Block{ $this->map(Ids::POLISHED_BASALT, function(Reader $in) : Block{
return Blocks::POLISHED_BASALT() return Blocks::POLISHED_BASALT()
@ -1241,14 +1324,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
default => throw $in->badValueException(StateNames::SPONGE_TYPE, $type), default => throw $in->badValueException(StateNames::SPONGE_TYPE, $type),
}); });
}); });
$this->map(Ids::SPRUCE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::SPRUCE_BUTTON(), $in));
$this->map(Ids::SPRUCE_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::SPRUCE_DOOR(), $in));
$this->map(Ids::SPRUCE_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::SPRUCE_FENCE_GATE(), $in));
$this->map(Ids::SPRUCE_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::SPRUCE_PRESSURE_PLATE(), $in));
$this->mapStairs(Ids::SPRUCE_STAIRS, fn() => Blocks::SPRUCE_STAIRS());
$this->map(Ids::SPRUCE_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::SPRUCE_SIGN(), $in));
$this->map(Ids::SPRUCE_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::SPRUCE_TRAPDOOR(), $in));
$this->map(Ids::SPRUCE_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::SPRUCE_WALL_SIGN(), $in));
$this->map(Ids::STAINED_GLASS, function(Reader $in) : Block{ $this->map(Ids::STAINED_GLASS, function(Reader $in) : Block{
return Blocks::STAINED_GLASS() return Blocks::STAINED_GLASS()
->setColor($in->readColor()); ->setColor($in->readColor());
@ -1265,7 +1340,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
return Blocks::BANNER() return Blocks::BANNER()
->setRotation($in->readBoundedInt(StateNames::GROUND_SIGN_DIRECTION, 0, 15)); ->setRotation($in->readBoundedInt(StateNames::GROUND_SIGN_DIRECTION, 0, 15));
}); });
$this->map(Ids::STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::OAK_SIGN(), $in));
$this->map(Ids::STONE, function(Reader $in) : Block{ $this->map(Ids::STONE, function(Reader $in) : Block{
return match($type = $in->readString(StateNames::STONE_TYPE)){ return match($type = $in->readString(StateNames::STONE_TYPE)){
StringValues::STONE_TYPE_ANDESITE => Blocks::ANDESITE(), StringValues::STONE_TYPE_ANDESITE => Blocks::ANDESITE(),
@ -1300,12 +1374,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
return Blocks::STONECUTTER() return Blocks::STONECUTTER()
->setFacing($in->readHorizontalFacing()); ->setFacing($in->readHorizontalFacing());
}); });
$this->map(Ids::STRIPPED_CRIMSON_HYPHAE, fn(Reader $in) => Helper::decodeLog(Blocks::CRIMSON_HYPHAE(), true, $in));
$this->map(Ids::STRIPPED_CRIMSON_STEM, fn(Reader $in) => Helper::decodeLog(Blocks::CRIMSON_STEM(), true, $in));
$this->map(Ids::STRIPPED_MANGROVE_LOG, fn(Reader $in) => Helper::decodeLog(Blocks::MANGROVE_LOG(), true, $in));
$this->map(Ids::STRIPPED_MANGROVE_WOOD, fn(Reader $in) => Helper::decodeLog(Blocks::MANGROVE_WOOD(), true, $in));
$this->map(Ids::STRIPPED_WARPED_HYPHAE, fn(Reader $in) => Helper::decodeLog(Blocks::WARPED_HYPHAE(), true, $in));
$this->map(Ids::STRIPPED_WARPED_STEM, fn(Reader $in) => Helper::decodeLog(Blocks::WARPED_STEM(), true, $in));
$this->map(Ids::SWEET_BERRY_BUSH, function(Reader $in) : Block{ $this->map(Ids::SWEET_BERRY_BUSH, function(Reader $in) : Block{
//berry bush only wants 0-3, but it can be bigger in MCPE due to misuse of GROWTH state which goes up to 7 //berry bush only wants 0-3, but it can be bigger in MCPE due to misuse of GROWTH state which goes up to 7
$growth = $in->readBoundedInt(StateNames::GROWTH, 0, 7); $growth = $in->readBoundedInt(StateNames::GROWTH, 0, 7);
@ -1328,7 +1396,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
return Blocks::TORCH() return Blocks::TORCH()
->setFacing($in->readTorchFacing()); ->setFacing($in->readTorchFacing());
}); });
$this->map(Ids::TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::OAK_TRAPDOOR(), $in));
$this->map(Ids::TRAPPED_CHEST, function(Reader $in) : Block{ $this->map(Ids::TRAPPED_CHEST, function(Reader $in) : Block{
return Blocks::TRAPPED_CHEST() return Blocks::TRAPPED_CHEST()
->setFacing($in->readHorizontalFacing()); ->setFacing($in->readHorizontalFacing());
@ -1375,18 +1442,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
return Blocks::WALL_BANNER() return Blocks::WALL_BANNER()
->setFacing($in->readHorizontalFacing()); ->setFacing($in->readHorizontalFacing());
}); });
$this->map(Ids::WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::OAK_WALL_SIGN(), $in));
$this->map(Ids::WARPED_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::WARPED_BUTTON(), $in));
$this->map(Ids::WARPED_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::WARPED_DOOR(), $in));
$this->mapSlab(Ids::WARPED_SLAB, Ids::WARPED_DOUBLE_SLAB, fn() => Blocks::WARPED_SLAB());
$this->map(Ids::WARPED_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::WARPED_FENCE_GATE(), $in));
$this->map(Ids::WARPED_HYPHAE, fn(Reader $in) => Helper::decodeLog(Blocks::WARPED_HYPHAE(), false, $in));
$this->map(Ids::WARPED_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::WARPED_PRESSURE_PLATE(), $in));
$this->mapStairs(Ids::WARPED_STAIRS, fn() => Blocks::WARPED_STAIRS());
$this->map(Ids::WARPED_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::WARPED_SIGN(), $in));
$this->map(Ids::WARPED_STEM, fn(Reader $in) => Helper::decodeLog(Blocks::WARPED_STEM(), false, $in));
$this->map(Ids::WARPED_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::WARPED_TRAPDOOR(), $in));
$this->map(Ids::WARPED_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::WARPED_WALL_SIGN(), $in));
$this->map(Ids::WATER, fn(Reader $in) => Helper::decodeStillLiquid(Blocks::WATER(), $in)); $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_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::NONE()));
$this->map(Ids::WAXED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::NONE())); $this->map(Ids::WAXED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::NONE()));
@ -1413,19 +1468,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
->setAge($in->readBoundedInt(StateNames::WEEPING_VINES_AGE, 0, 25)); ->setAge($in->readBoundedInt(StateNames::WEEPING_VINES_AGE, 0, 25));
}); });
$this->map(Ids::WHEAT, fn(Reader $in) => Helper::decodeCrops(Blocks::WHEAT(), $in)); $this->map(Ids::WHEAT, fn(Reader $in) => Helper::decodeCrops(Blocks::WHEAT(), $in));
$this->map(Ids::WOOD, fn(Reader $in) : Block => Helper::decodeLog(match($woodType = $in->readString(StateNames::WOOD_TYPE)){
StringValues::WOOD_TYPE_ACACIA => Blocks::ACACIA_WOOD(),
StringValues::WOOD_TYPE_BIRCH => Blocks::BIRCH_WOOD(),
StringValues::WOOD_TYPE_DARK_OAK => Blocks::DARK_OAK_WOOD(),
StringValues::WOOD_TYPE_JUNGLE => Blocks::JUNGLE_WOOD(),
StringValues::WOOD_TYPE_OAK => Blocks::OAK_WOOD(),
StringValues::WOOD_TYPE_SPRUCE => Blocks::SPRUCE_WOOD(),
default => throw $in->badValueException(StateNames::WOOD_TYPE, $woodType),
}, $in->readBool(StateNames::STRIPPED_BIT), $in));
$this->map(Ids::WOODEN_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::OAK_BUTTON(), $in));
$this->map(Ids::WOODEN_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::OAK_DOOR(), $in));
$this->map(Ids::WOODEN_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::OAK_PRESSURE_PLATE(), $in));
$this->mapSlab(Ids::WOODEN_SLAB, Ids::DOUBLE_WOODEN_SLAB, fn(Reader $in) => Helper::mapWoodenSlabType($in));
} }
/** @throws BlockStateDeserializeException */ /** @throws BlockStateDeserializeException */

View File

@ -29,6 +29,7 @@ use pocketmine\block\utils\DyeColor;
use pocketmine\block\utils\SlabType; use pocketmine\block\utils\SlabType;
use pocketmine\block\utils\WallConnectionType; use pocketmine\block\utils\WallConnectionType;
use pocketmine\block\utils\WoodType; use pocketmine\block\utils\WoodType;
use pocketmine\data\bedrock\block\BlockLegacyMetadata;
use pocketmine\data\bedrock\block\BlockStateData; use pocketmine\data\bedrock\block\BlockStateData;
use pocketmine\data\bedrock\block\BlockStateNames; use pocketmine\data\bedrock\block\BlockStateNames;
use pocketmine\data\bedrock\block\BlockStateSerializeException; use pocketmine\data\bedrock\block\BlockStateSerializeException;
@ -39,6 +40,7 @@ use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\StringTag; use pocketmine\nbt\tag\StringTag;
use pocketmine\nbt\tag\Tag; use pocketmine\nbt\tag\Tag;
use pocketmine\utils\AssumptionFailedError;
final class BlockStateWriter{ final class BlockStateWriter{
@ -88,6 +90,28 @@ final class BlockStateWriter{
return $this; return $this;
} }
/**
* @param int[] $faces
* @phpstan-param array<int, int> $faces
* @return $this
*/
public function writeFacingFlags(array $faces) : self{
$result = 0;
foreach($faces as $face){
$result |= match($face){
Facing::DOWN => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_DOWN,
Facing::UP => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_UP,
Facing::NORTH => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_NORTH,
Facing::SOUTH => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_SOUTH,
Facing::WEST => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_WEST,
Facing::EAST => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_EAST,
default => throw new AssumptionFailedError("Unhandled face $face")
};
}
return $this->writeInt(BlockStateNames::MULTI_FACE_DIRECTION_BITS, $result);
}
/** @return $this */ /** @return $this */
public function writeEndRodFacingDirection(int $value) : self{ public function writeEndRodFacingDirection(int $value) : self{
//end rods are stupid in bedrock and have everything except up/down the wrong way round //end rods are stupid in bedrock and have everything except up/down the wrong way round

View File

@ -24,6 +24,9 @@ declare(strict_types=1);
namespace pocketmine\data\bedrock\block\upgrade; namespace pocketmine\data\bedrock\block\upgrade;
use pocketmine\nbt\tag\Tag; use pocketmine\nbt\tag\Tag;
use pocketmine\utils\Utils;
use function array_diff;
use function count;
final class BlockStateUpgradeSchemaBlockRemap{ final class BlockStateUpgradeSchemaBlockRemap{
/** /**
@ -37,8 +40,43 @@ final class BlockStateUpgradeSchemaBlockRemap{
*/ */
public function __construct( public function __construct(
public array $oldState, public array $oldState,
public string $newName, public string|BlockStateUpgradeSchemaFlattenedName $newName,
public array $newState, public array $newState,
public array $copiedState public array $copiedState
){} ){}
public function equals(self $that) : bool{
$sameName = $this->newName === $that->newName ||
(
$this->newName instanceof BlockStateUpgradeSchemaFlattenedName &&
$that->newName instanceof BlockStateUpgradeSchemaFlattenedName &&
$this->newName->equals($that->newName)
);
if(!$sameName){
return false;
}
if(
count($this->oldState) !== count($that->oldState) ||
count($this->newState) !== count($that->newState) ||
count($this->copiedState) !== count($that->copiedState) ||
count(array_diff($this->copiedState, $that->copiedState)) !== 0
){
return false;
}
foreach(Utils::stringifyKeys($this->oldState) as $propertyName => $propertyValue){
if(!isset($that->oldState[$propertyName]) || !$that->oldState[$propertyName]->equals($propertyValue)){
//different filter value
return false;
}
}
foreach(Utils::stringifyKeys($this->newState) as $propertyName => $propertyValue){
if(!isset($that->newState[$propertyName]) || !$that->newState[$propertyName]->equals($propertyValue)){
//different replacement value
return false;
}
}
return true;
}
} }

View File

@ -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\data\bedrock\block\upgrade;
final class BlockStateUpgradeSchemaFlattenedName{
public function __construct(
public string $prefix,
public string $flattenedProperty,
public string $suffix
){}
public function equals(self $that) : bool{
return $this->prefix === $that->prefix &&
$this->flattenedProperty === $that->flattenedProperty &&
$this->suffix === $that->suffix;
}
}

View File

@ -25,6 +25,7 @@ namespace pocketmine\data\bedrock\block\upgrade;
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModel; use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModel;
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelBlockRemap; use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelBlockRemap;
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelFlattenedName;
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelTag; use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelTag;
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelValueRemap; use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelValueRemap;
use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\ByteTag;
@ -43,12 +44,14 @@ use function get_debug_type;
use function gettype; use function gettype;
use function implode; use function implode;
use function is_object; use function is_object;
use function is_string;
use function json_decode; use function json_decode;
use function json_encode; use function json_encode;
use function ksort; use function ksort;
use function sort; use function sort;
use function str_pad; use function str_pad;
use function strval; use function strval;
use function usort;
use const JSON_THROW_ON_ERROR; use const JSON_THROW_ON_ERROR;
use const SORT_NUMERIC; use const SORT_NUMERIC;
use const STR_PAD_LEFT; use const STR_PAD_LEFT;
@ -154,9 +157,17 @@ final class BlockStateUpgradeSchemaUtils{
foreach(Utils::stringifyKeys($model->remappedStates ?? []) as $oldBlockName => $remaps){ foreach(Utils::stringifyKeys($model->remappedStates ?? []) as $oldBlockName => $remaps){
foreach($remaps as $remap){ foreach($remaps as $remap){
if(isset($remap->newName) === isset($remap->newFlattenedName)){
throw new \UnexpectedValueException("Expected exactly one of 'newName' or 'newFlattenedName' properties to be set");
}
$result->remappedStates[$oldBlockName][] = new BlockStateUpgradeSchemaBlockRemap( $result->remappedStates[$oldBlockName][] = new BlockStateUpgradeSchemaBlockRemap(
array_map(fn(BlockStateUpgradeSchemaModelTag $tag) => self::jsonModelToTag($tag), $remap->oldState ?? []), array_map(fn(BlockStateUpgradeSchemaModelTag $tag) => self::jsonModelToTag($tag), $remap->oldState ?? []),
$remap->newName, $remap->newName ?? new BlockStateUpgradeSchemaFlattenedName(
$remap->newFlattenedName->prefix,
$remap->newFlattenedName->flattenedProperty,
$remap->newFlattenedName->suffix
),
array_map(fn(BlockStateUpgradeSchemaModelTag $tag) => self::jsonModelToTag($tag), $remap->newState ?? []), array_map(fn(BlockStateUpgradeSchemaModelTag $tag) => self::jsonModelToTag($tag), $remap->newState ?? []),
$remap->copiedState ?? [] $remap->copiedState ?? []
); );
@ -285,7 +296,13 @@ final class BlockStateUpgradeSchemaUtils{
foreach($remaps as $remap){ foreach($remaps as $remap){
$modelRemap = new BlockStateUpgradeSchemaModelBlockRemap( $modelRemap = new BlockStateUpgradeSchemaModelBlockRemap(
array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->oldState), array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->oldState),
$remap->newName, is_string($remap->newName) ?
$remap->newName :
new BlockStateUpgradeSchemaModelFlattenedName(
$remap->newName->prefix,
$remap->newName->flattenedProperty,
$remap->newName->suffix
),
array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->newState), array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->newState),
$remap->copiedState $remap->copiedState
); );
@ -299,7 +316,15 @@ final class BlockStateUpgradeSchemaUtils{
} }
$keyedRemaps[$key] = $modelRemap; $keyedRemaps[$key] = $modelRemap;
} }
ksort($keyedRemaps); usort($keyedRemaps, function(BlockStateUpgradeSchemaModelBlockRemap $a, BlockStateUpgradeSchemaModelBlockRemap $b) : int{
//remaps with more specific criteria must come first
$filterSizeCompare = count($b->oldState ?? []) <=> count($a->oldState ?? []);
if($filterSizeCompare !== 0){
return $filterSizeCompare;
}
//remaps with the same number of criteria should be sorted alphabetically, but this is not strictly necessary
return json_encode($a->oldState ?? []) <=> json_encode($b->oldState ?? []);
});
$result->remappedStates[$oldBlockName] = array_values($keyedRemaps); $result->remappedStates[$oldBlockName] = array_values($keyedRemaps);
} }
if(isset($result->remappedStates)){ if(isset($result->remappedStates)){

View File

@ -24,11 +24,14 @@ declare(strict_types=1);
namespace pocketmine\data\bedrock\block\upgrade; namespace pocketmine\data\bedrock\block\upgrade;
use pocketmine\data\bedrock\block\BlockStateData; use pocketmine\data\bedrock\block\BlockStateData;
use pocketmine\nbt\tag\StringTag;
use pocketmine\nbt\tag\Tag; use pocketmine\nbt\tag\Tag;
use pocketmine\utils\Utils; use pocketmine\utils\Utils;
use function count; use function count;
use function is_string;
use function ksort; use function ksort;
use function max; use function max;
use function sprintf;
use const SORT_NUMERIC; use const SORT_NUMERIC;
final class BlockStateUpgrader{ final class BlockStateUpgrader{
@ -79,6 +82,20 @@ final class BlockStateUpgrader{
continue 2; //try next state continue 2; //try next state
} }
} }
if(is_string($remap->newName)){
$newName = $remap->newName;
}else{
$flattenedValue = $oldState[$remap->newName->flattenedProperty];
if($flattenedValue instanceof StringTag){
$newName = sprintf("%s%s%s", $remap->newName->prefix, $flattenedValue->getValue(), $remap->newName->suffix);
unset($oldState[$remap->newName->flattenedProperty]);
}else{
//flattened property is not a TAG_String, so this transformation is not applicable
continue;
}
}
$newState = $remap->newState; $newState = $remap->newState;
foreach($remap->copiedState as $stateName){ foreach($remap->copiedState as $stateName){
if(isset($oldState[$stateName])){ if(isset($oldState[$stateName])){
@ -86,7 +103,7 @@ final class BlockStateUpgrader{
} }
} }
$blockStateData = new BlockStateData($remap->newName, $newState, $resultVersion); $blockStateData = new BlockStateData($newName, $newState, $resultVersion);
continue 2; //try next schema continue 2; //try next schema
} }
} }

View File

@ -34,8 +34,16 @@ final class BlockStateUpgradeSchemaModelBlockRemap{
*/ */
public ?array $oldState; public ?array $oldState;
/** @required */ /**
* Either this or newFlattenedName must be present
* Due to technical limitations of jsonmapper, we can't use a union type here
*/
public string $newName; public string $newName;
/**
* Either this or newName must be present
* Due to technical limitations of jsonmapper, we can't use a union type here
*/
public BlockStateUpgradeSchemaModelFlattenedName $newFlattenedName;
/** /**
* @var BlockStateUpgradeSchemaModelTag[]|null * @var BlockStateUpgradeSchemaModelTag[]|null
@ -59,9 +67,13 @@ final class BlockStateUpgradeSchemaModelBlockRemap{
* @phpstan-param array<string, BlockStateUpgradeSchemaModelTag> $newState * @phpstan-param array<string, BlockStateUpgradeSchemaModelTag> $newState
* @phpstan-param list<string> $copiedState * @phpstan-param list<string> $copiedState
*/ */
public function __construct(array $oldState, string $newName, array $newState, array $copiedState){ public function __construct(array $oldState, string|BlockStateUpgradeSchemaModelFlattenedName $newNameRule, array $newState, array $copiedState){
$this->oldState = count($oldState) === 0 ? null : $oldState; $this->oldState = count($oldState) === 0 ? null : $oldState;
$this->newName = $newName; if($newNameRule instanceof BlockStateUpgradeSchemaModelFlattenedName){
$this->newFlattenedName = $newNameRule;
}else{
$this->newName = $newNameRule;
}
$this->newState = count($newState) === 0 ? null : $newState; $this->newState = count($newState) === 0 ? null : $newState;
$this->copiedState = $copiedState; $this->copiedState = $copiedState;
} }

View File

@ -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\data\bedrock\block\upgrade\model;
final class BlockStateUpgradeSchemaModelFlattenedName{
/** @required */
public string $prefix;
/** @required */
public string $flattenedProperty;
/** @required */
public string $suffix;
public function __construct(string $prefix, string $flattenedProperty, string $suffix){
$this->prefix = $prefix;
$this->flattenedProperty = $flattenedProperty;
$this->suffix = $suffix;
}
}

View File

@ -135,6 +135,7 @@ final class ItemSerializerDeserializerRegistrar{
$this->map1to1Block(Ids::CAKE, Blocks::CAKE()); $this->map1to1Block(Ids::CAKE, Blocks::CAKE());
$this->map1to1Block(Ids::CAULDRON, Blocks::CAULDRON()); $this->map1to1Block(Ids::CAULDRON, Blocks::CAULDRON());
$this->map1to1Block(Ids::CHAIN, Blocks::CHAIN()); $this->map1to1Block(Ids::CHAIN, Blocks::CHAIN());
$this->map1to1Block(Ids::CHERRY_DOOR, Blocks::CHERRY_DOOR());
$this->map1to1Block(Ids::COMPARATOR, Blocks::REDSTONE_COMPARATOR()); $this->map1to1Block(Ids::COMPARATOR, Blocks::REDSTONE_COMPARATOR());
$this->map1to1Block(Ids::CRIMSON_DOOR, Blocks::CRIMSON_DOOR()); $this->map1to1Block(Ids::CRIMSON_DOOR, Blocks::CRIMSON_DOOR());
$this->map1to1Block(Ids::DARK_OAK_DOOR, Blocks::DARK_OAK_DOOR()); $this->map1to1Block(Ids::DARK_OAK_DOOR, Blocks::DARK_OAK_DOOR());
@ -185,6 +186,7 @@ final class ItemSerializerDeserializerRegistrar{
$this->map1to1Item(Ids::CHAINMAIL_HELMET, Items::CHAINMAIL_HELMET()); $this->map1to1Item(Ids::CHAINMAIL_HELMET, Items::CHAINMAIL_HELMET());
$this->map1to1Item(Ids::CHAINMAIL_LEGGINGS, Items::CHAINMAIL_LEGGINGS()); $this->map1to1Item(Ids::CHAINMAIL_LEGGINGS, Items::CHAINMAIL_LEGGINGS());
$this->map1to1Item(Ids::CHARCOAL, Items::CHARCOAL()); $this->map1to1Item(Ids::CHARCOAL, Items::CHARCOAL());
$this->map1to1Item(Ids::CHERRY_SIGN, Items::CHERRY_SIGN());
$this->map1to1Item(Ids::CHICKEN, Items::RAW_CHICKEN()); $this->map1to1Item(Ids::CHICKEN, Items::RAW_CHICKEN());
$this->map1to1Item(Ids::CHORUS_FRUIT, Items::CHORUS_FRUIT()); $this->map1to1Item(Ids::CHORUS_FRUIT, Items::CHORUS_FRUIT());
$this->map1to1Item(Ids::CLAY_BALL, Items::CLAY()); $this->map1to1Item(Ids::CLAY_BALL, Items::CLAY());

View File

@ -30,20 +30,24 @@ final class ItemTypeNames{
public const ACACIA_BOAT = "minecraft:acacia_boat"; public const ACACIA_BOAT = "minecraft:acacia_boat";
public const ACACIA_CHEST_BOAT = "minecraft:acacia_chest_boat"; public const ACACIA_CHEST_BOAT = "minecraft:acacia_chest_boat";
public const ACACIA_DOOR = "minecraft:acacia_door"; public const ACACIA_DOOR = "minecraft:acacia_door";
public const ACACIA_HANGING_SIGN = "minecraft:acacia_hanging_sign";
public const ACACIA_SIGN = "minecraft:acacia_sign"; public const ACACIA_SIGN = "minecraft:acacia_sign";
public const AGENT_SPAWN_EGG = "minecraft:agent_spawn_egg"; public const AGENT_SPAWN_EGG = "minecraft:agent_spawn_egg";
public const ALLAY_SPAWN_EGG = "minecraft:allay_spawn_egg"; public const ALLAY_SPAWN_EGG = "minecraft:allay_spawn_egg";
public const AMETHYST_SHARD = "minecraft:amethyst_shard"; public const AMETHYST_SHARD = "minecraft:amethyst_shard";
public const ANGLER_POTTERY_SHERD = "minecraft:angler_pottery_sherd";
public const APPLE = "minecraft:apple"; public const APPLE = "minecraft:apple";
public const ARCHER_POTTERY_SHARD = "minecraft:archer_pottery_shard"; public const ARCHER_POTTERY_SHERD = "minecraft:archer_pottery_sherd";
public const ARMOR_STAND = "minecraft:armor_stand"; public const ARMOR_STAND = "minecraft:armor_stand";
public const ARMS_UP_POTTERY_SHARD = "minecraft:arms_up_pottery_shard"; public const ARMS_UP_POTTERY_SHERD = "minecraft:arms_up_pottery_sherd";
public const ARROW = "minecraft:arrow"; public const ARROW = "minecraft:arrow";
public const AXOLOTL_BUCKET = "minecraft:axolotl_bucket"; public const AXOLOTL_BUCKET = "minecraft:axolotl_bucket";
public const AXOLOTL_SPAWN_EGG = "minecraft:axolotl_spawn_egg"; public const AXOLOTL_SPAWN_EGG = "minecraft:axolotl_spawn_egg";
public const BAKED_POTATO = "minecraft:baked_potato"; public const BAKED_POTATO = "minecraft:baked_potato";
public const BALLOON = "minecraft:balloon"; public const BALLOON = "minecraft:balloon";
public const BAMBOO_CHEST_RAFT = "minecraft:bamboo_chest_raft"; public const BAMBOO_CHEST_RAFT = "minecraft:bamboo_chest_raft";
public const BAMBOO_DOOR = "minecraft:bamboo_door";
public const BAMBOO_HANGING_SIGN = "minecraft:bamboo_hanging_sign";
public const BAMBOO_RAFT = "minecraft:bamboo_raft"; public const BAMBOO_RAFT = "minecraft:bamboo_raft";
public const BAMBOO_SIGN = "minecraft:bamboo_sign"; public const BAMBOO_SIGN = "minecraft:bamboo_sign";
public const BANNER = "minecraft:banner"; public const BANNER = "minecraft:banner";
@ -58,8 +62,10 @@ final class ItemTypeNames{
public const BIRCH_BOAT = "minecraft:birch_boat"; public const BIRCH_BOAT = "minecraft:birch_boat";
public const BIRCH_CHEST_BOAT = "minecraft:birch_chest_boat"; public const BIRCH_CHEST_BOAT = "minecraft:birch_chest_boat";
public const BIRCH_DOOR = "minecraft:birch_door"; public const BIRCH_DOOR = "minecraft:birch_door";
public const BIRCH_HANGING_SIGN = "minecraft:birch_hanging_sign";
public const BIRCH_SIGN = "minecraft:birch_sign"; public const BIRCH_SIGN = "minecraft:birch_sign";
public const BLACK_DYE = "minecraft:black_dye"; public const BLACK_DYE = "minecraft:black_dye";
public const BLADE_POTTERY_SHERD = "minecraft:blade_pottery_sherd";
public const BLAZE_POWDER = "minecraft:blaze_powder"; public const BLAZE_POWDER = "minecraft:blaze_powder";
public const BLAZE_ROD = "minecraft:blaze_rod"; public const BLAZE_ROD = "minecraft:blaze_rod";
public const BLAZE_SPAWN_EGG = "minecraft:blaze_spawn_egg"; public const BLAZE_SPAWN_EGG = "minecraft:blaze_spawn_egg";
@ -73,15 +79,18 @@ final class ItemTypeNames{
public const BOW = "minecraft:bow"; public const BOW = "minecraft:bow";
public const BOWL = "minecraft:bowl"; public const BOWL = "minecraft:bowl";
public const BREAD = "minecraft:bread"; public const BREAD = "minecraft:bread";
public const BREWER_POTTERY_SHERD = "minecraft:brewer_pottery_sherd";
public const BREWING_STAND = "minecraft:brewing_stand"; public const BREWING_STAND = "minecraft:brewing_stand";
public const BRICK = "minecraft:brick"; public const BRICK = "minecraft:brick";
public const BROWN_DYE = "minecraft:brown_dye"; public const BROWN_DYE = "minecraft:brown_dye";
public const BRUSH = "minecraft:brush"; public const BRUSH = "minecraft:brush";
public const BUCKET = "minecraft:bucket"; public const BUCKET = "minecraft:bucket";
public const BURN_POTTERY_SHERD = "minecraft:burn_pottery_sherd";
public const CAKE = "minecraft:cake"; public const CAKE = "minecraft:cake";
public const CAMEL_SPAWN_EGG = "minecraft:camel_spawn_egg"; public const CAMEL_SPAWN_EGG = "minecraft:camel_spawn_egg";
public const CAMERA = "minecraft:camera"; public const CAMERA = "minecraft:camera";
public const CAMPFIRE = "minecraft:campfire"; public const CAMPFIRE = "minecraft:campfire";
public const CARPET = "minecraft:carpet";
public const CARROT = "minecraft:carrot"; public const CARROT = "minecraft:carrot";
public const CARROT_ON_A_STICK = "minecraft:carrot_on_a_stick"; public const CARROT_ON_A_STICK = "minecraft:carrot_on_a_stick";
public const CAT_SPAWN_EGG = "minecraft:cat_spawn_egg"; public const CAT_SPAWN_EGG = "minecraft:cat_spawn_egg";
@ -93,6 +102,11 @@ final class ItemTypeNames{
public const CHAINMAIL_HELMET = "minecraft:chainmail_helmet"; public const CHAINMAIL_HELMET = "minecraft:chainmail_helmet";
public const CHAINMAIL_LEGGINGS = "minecraft:chainmail_leggings"; public const CHAINMAIL_LEGGINGS = "minecraft:chainmail_leggings";
public const CHARCOAL = "minecraft:charcoal"; public const CHARCOAL = "minecraft:charcoal";
public const CHERRY_BOAT = "minecraft:cherry_boat";
public const CHERRY_CHEST_BOAT = "minecraft:cherry_chest_boat";
public const CHERRY_DOOR = "minecraft:cherry_door";
public const CHERRY_HANGING_SIGN = "minecraft:cherry_hanging_sign";
public const CHERRY_SIGN = "minecraft:cherry_sign";
public const CHEST_BOAT = "minecraft:chest_boat"; public const CHEST_BOAT = "minecraft:chest_boat";
public const CHEST_MINECART = "minecraft:chest_minecart"; public const CHEST_MINECART = "minecraft:chest_minecart";
public const CHICKEN = "minecraft:chicken"; public const CHICKEN = "minecraft:chicken";
@ -101,6 +115,7 @@ final class ItemTypeNames{
public const CLAY_BALL = "minecraft:clay_ball"; public const CLAY_BALL = "minecraft:clay_ball";
public const CLOCK = "minecraft:clock"; public const CLOCK = "minecraft:clock";
public const COAL = "minecraft:coal"; public const COAL = "minecraft:coal";
public const COAST_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:coast_armor_trim_smithing_template";
public const COCOA_BEANS = "minecraft:cocoa_beans"; public const COCOA_BEANS = "minecraft:cocoa_beans";
public const COD = "minecraft:cod"; public const COD = "minecraft:cod";
public const COD_BUCKET = "minecraft:cod_bucket"; public const COD_BUCKET = "minecraft:cod_bucket";
@ -118,18 +133,21 @@ final class ItemTypeNames{
public const COOKED_SALMON = "minecraft:cooked_salmon"; public const COOKED_SALMON = "minecraft:cooked_salmon";
public const COOKIE = "minecraft:cookie"; public const COOKIE = "minecraft:cookie";
public const COPPER_INGOT = "minecraft:copper_ingot"; public const COPPER_INGOT = "minecraft:copper_ingot";
public const CORAL = "minecraft:coral";
public const COW_SPAWN_EGG = "minecraft:cow_spawn_egg"; public const COW_SPAWN_EGG = "minecraft:cow_spawn_egg";
public const CREEPER_BANNER_PATTERN = "minecraft:creeper_banner_pattern"; public const CREEPER_BANNER_PATTERN = "minecraft:creeper_banner_pattern";
public const CREEPER_SPAWN_EGG = "minecraft:creeper_spawn_egg"; public const CREEPER_SPAWN_EGG = "minecraft:creeper_spawn_egg";
public const CRIMSON_DOOR = "minecraft:crimson_door"; public const CRIMSON_DOOR = "minecraft:crimson_door";
public const CRIMSON_HANGING_SIGN = "minecraft:crimson_hanging_sign";
public const CRIMSON_SIGN = "minecraft:crimson_sign"; public const CRIMSON_SIGN = "minecraft:crimson_sign";
public const CROSSBOW = "minecraft:crossbow"; public const CROSSBOW = "minecraft:crossbow";
public const CYAN_DYE = "minecraft:cyan_dye"; public const CYAN_DYE = "minecraft:cyan_dye";
public const DANGER_POTTERY_SHERD = "minecraft:danger_pottery_sherd";
public const DARK_OAK_BOAT = "minecraft:dark_oak_boat"; public const DARK_OAK_BOAT = "minecraft:dark_oak_boat";
public const DARK_OAK_CHEST_BOAT = "minecraft:dark_oak_chest_boat"; public const DARK_OAK_CHEST_BOAT = "minecraft:dark_oak_chest_boat";
public const DARK_OAK_DOOR = "minecraft:dark_oak_door"; public const DARK_OAK_DOOR = "minecraft:dark_oak_door";
public const DARK_OAK_HANGING_SIGN = "minecraft:dark_oak_hanging_sign";
public const DARK_OAK_SIGN = "minecraft:dark_oak_sign"; public const DARK_OAK_SIGN = "minecraft:dark_oak_sign";
public const DEBUG_STICK = "minecraft:debug_stick";
public const DIAMOND = "minecraft:diamond"; public const DIAMOND = "minecraft:diamond";
public const DIAMOND_AXE = "minecraft:diamond_axe"; public const DIAMOND_AXE = "minecraft:diamond_axe";
public const DIAMOND_BOOTS = "minecraft:diamond_boots"; public const DIAMOND_BOOTS = "minecraft:diamond_boots";
@ -147,6 +165,7 @@ final class ItemTypeNames{
public const DRAGON_BREATH = "minecraft:dragon_breath"; public const DRAGON_BREATH = "minecraft:dragon_breath";
public const DRIED_KELP = "minecraft:dried_kelp"; public const DRIED_KELP = "minecraft:dried_kelp";
public const DROWNED_SPAWN_EGG = "minecraft:drowned_spawn_egg"; public const DROWNED_SPAWN_EGG = "minecraft:drowned_spawn_egg";
public const DUNE_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:dune_armor_trim_smithing_template";
public const DYE = "minecraft:dye"; public const DYE = "minecraft:dye";
public const ECHO_SHARD = "minecraft:echo_shard"; public const ECHO_SHARD = "minecraft:echo_shard";
public const EGG = "minecraft:egg"; public const EGG = "minecraft:egg";
@ -164,7 +183,10 @@ final class ItemTypeNames{
public const ENDERMITE_SPAWN_EGG = "minecraft:endermite_spawn_egg"; public const ENDERMITE_SPAWN_EGG = "minecraft:endermite_spawn_egg";
public const EVOKER_SPAWN_EGG = "minecraft:evoker_spawn_egg"; public const EVOKER_SPAWN_EGG = "minecraft:evoker_spawn_egg";
public const EXPERIENCE_BOTTLE = "minecraft:experience_bottle"; public const EXPERIENCE_BOTTLE = "minecraft:experience_bottle";
public const EXPLORER_POTTERY_SHERD = "minecraft:explorer_pottery_sherd";
public const EYE_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:eye_armor_trim_smithing_template";
public const FEATHER = "minecraft:feather"; public const FEATHER = "minecraft:feather";
public const FENCE = "minecraft:fence";
public const FERMENTED_SPIDER_EYE = "minecraft:fermented_spider_eye"; public const FERMENTED_SPIDER_EYE = "minecraft:fermented_spider_eye";
public const FIELD_MASONED_BANNER_PATTERN = "minecraft:field_masoned_banner_pattern"; public const FIELD_MASONED_BANNER_PATTERN = "minecraft:field_masoned_banner_pattern";
public const FILLED_MAP = "minecraft:filled_map"; public const FILLED_MAP = "minecraft:filled_map";
@ -178,6 +200,7 @@ final class ItemTypeNames{
public const FLOWER_POT = "minecraft:flower_pot"; public const FLOWER_POT = "minecraft:flower_pot";
public const FOX_SPAWN_EGG = "minecraft:fox_spawn_egg"; public const FOX_SPAWN_EGG = "minecraft:fox_spawn_egg";
public const FRAME = "minecraft:frame"; public const FRAME = "minecraft:frame";
public const FRIEND_POTTERY_SHERD = "minecraft:friend_pottery_sherd";
public const FROG_SPAWN_EGG = "minecraft:frog_spawn_egg"; public const FROG_SPAWN_EGG = "minecraft:frog_spawn_egg";
public const GHAST_SPAWN_EGG = "minecraft:ghast_spawn_egg"; public const GHAST_SPAWN_EGG = "minecraft:ghast_spawn_egg";
public const GHAST_TEAR = "minecraft:ghast_tear"; public const GHAST_TEAR = "minecraft:ghast_tear";
@ -211,12 +234,16 @@ final class ItemTypeNames{
public const GUARDIAN_SPAWN_EGG = "minecraft:guardian_spawn_egg"; public const GUARDIAN_SPAWN_EGG = "minecraft:guardian_spawn_egg";
public const GUNPOWDER = "minecraft:gunpowder"; public const GUNPOWDER = "minecraft:gunpowder";
public const HEART_OF_THE_SEA = "minecraft:heart_of_the_sea"; public const HEART_OF_THE_SEA = "minecraft:heart_of_the_sea";
public const HEART_POTTERY_SHERD = "minecraft:heart_pottery_sherd";
public const HEARTBREAK_POTTERY_SHERD = "minecraft:heartbreak_pottery_sherd";
public const HOGLIN_SPAWN_EGG = "minecraft:hoglin_spawn_egg"; public const HOGLIN_SPAWN_EGG = "minecraft:hoglin_spawn_egg";
public const HONEY_BOTTLE = "minecraft:honey_bottle"; public const HONEY_BOTTLE = "minecraft:honey_bottle";
public const HONEYCOMB = "minecraft:honeycomb"; public const HONEYCOMB = "minecraft:honeycomb";
public const HOPPER = "minecraft:hopper"; public const HOPPER = "minecraft:hopper";
public const HOPPER_MINECART = "minecraft:hopper_minecart"; public const HOPPER_MINECART = "minecraft:hopper_minecart";
public const HORSE_SPAWN_EGG = "minecraft:horse_spawn_egg"; public const HORSE_SPAWN_EGG = "minecraft:horse_spawn_egg";
public const HOST_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:host_armor_trim_smithing_template";
public const HOWL_POTTERY_SHERD = "minecraft:howl_pottery_sherd";
public const HUSK_SPAWN_EGG = "minecraft:husk_spawn_egg"; public const HUSK_SPAWN_EGG = "minecraft:husk_spawn_egg";
public const ICE_BOMB = "minecraft:ice_bomb"; public const ICE_BOMB = "minecraft:ice_bomb";
public const INK_SAC = "minecraft:ink_sac"; public const INK_SAC = "minecraft:ink_sac";
@ -237,6 +264,7 @@ final class ItemTypeNames{
public const JUNGLE_BOAT = "minecraft:jungle_boat"; public const JUNGLE_BOAT = "minecraft:jungle_boat";
public const JUNGLE_CHEST_BOAT = "minecraft:jungle_chest_boat"; public const JUNGLE_CHEST_BOAT = "minecraft:jungle_chest_boat";
public const JUNGLE_DOOR = "minecraft:jungle_door"; public const JUNGLE_DOOR = "minecraft:jungle_door";
public const JUNGLE_HANGING_SIGN = "minecraft:jungle_hanging_sign";
public const JUNGLE_SIGN = "minecraft:jungle_sign"; public const JUNGLE_SIGN = "minecraft:jungle_sign";
public const KELP = "minecraft:kelp"; public const KELP = "minecraft:kelp";
public const LAPIS_LAZULI = "minecraft:lapis_lazuli"; public const LAPIS_LAZULI = "minecraft:lapis_lazuli";
@ -254,20 +282,25 @@ final class ItemTypeNames{
public const LINGERING_POTION = "minecraft:lingering_potion"; public const LINGERING_POTION = "minecraft:lingering_potion";
public const LLAMA_SPAWN_EGG = "minecraft:llama_spawn_egg"; public const LLAMA_SPAWN_EGG = "minecraft:llama_spawn_egg";
public const LODESTONE_COMPASS = "minecraft:lodestone_compass"; public const LODESTONE_COMPASS = "minecraft:lodestone_compass";
public const LOG = "minecraft:log";
public const LOG2 = "minecraft:log2";
public const MAGENTA_DYE = "minecraft:magenta_dye"; public const MAGENTA_DYE = "minecraft:magenta_dye";
public const MAGMA_CREAM = "minecraft:magma_cream"; public const MAGMA_CREAM = "minecraft:magma_cream";
public const MAGMA_CUBE_SPAWN_EGG = "minecraft:magma_cube_spawn_egg"; public const MAGMA_CUBE_SPAWN_EGG = "minecraft:magma_cube_spawn_egg";
public const MANGROVE_BOAT = "minecraft:mangrove_boat"; public const MANGROVE_BOAT = "minecraft:mangrove_boat";
public const MANGROVE_CHEST_BOAT = "minecraft:mangrove_chest_boat"; public const MANGROVE_CHEST_BOAT = "minecraft:mangrove_chest_boat";
public const MANGROVE_DOOR = "minecraft:mangrove_door"; public const MANGROVE_DOOR = "minecraft:mangrove_door";
public const MANGROVE_HANGING_SIGN = "minecraft:mangrove_hanging_sign";
public const MANGROVE_SIGN = "minecraft:mangrove_sign"; public const MANGROVE_SIGN = "minecraft:mangrove_sign";
public const MEDICINE = "minecraft:medicine"; public const MEDICINE = "minecraft:medicine";
public const MELON_SEEDS = "minecraft:melon_seeds"; public const MELON_SEEDS = "minecraft:melon_seeds";
public const MELON_SLICE = "minecraft:melon_slice"; public const MELON_SLICE = "minecraft:melon_slice";
public const MILK_BUCKET = "minecraft:milk_bucket"; public const MILK_BUCKET = "minecraft:milk_bucket";
public const MINECART = "minecraft:minecart"; public const MINECART = "minecraft:minecart";
public const MINER_POTTERY_SHERD = "minecraft:miner_pottery_sherd";
public const MOJANG_BANNER_PATTERN = "minecraft:mojang_banner_pattern"; public const MOJANG_BANNER_PATTERN = "minecraft:mojang_banner_pattern";
public const MOOSHROOM_SPAWN_EGG = "minecraft:mooshroom_spawn_egg"; public const MOOSHROOM_SPAWN_EGG = "minecraft:mooshroom_spawn_egg";
public const MOURNER_POTTERY_SHERD = "minecraft:mourner_pottery_sherd";
public const MULE_SPAWN_EGG = "minecraft:mule_spawn_egg"; public const MULE_SPAWN_EGG = "minecraft:mule_spawn_egg";
public const MUSHROOM_STEW = "minecraft:mushroom_stew"; public const MUSHROOM_STEW = "minecraft:mushroom_stew";
public const MUSIC_DISC_11 = "minecraft:music_disc_11"; public const MUSIC_DISC_11 = "minecraft:music_disc_11";
@ -281,6 +314,7 @@ final class ItemTypeNames{
public const MUSIC_DISC_MELLOHI = "minecraft:music_disc_mellohi"; public const MUSIC_DISC_MELLOHI = "minecraft:music_disc_mellohi";
public const MUSIC_DISC_OTHERSIDE = "minecraft:music_disc_otherside"; public const MUSIC_DISC_OTHERSIDE = "minecraft:music_disc_otherside";
public const MUSIC_DISC_PIGSTEP = "minecraft:music_disc_pigstep"; public const MUSIC_DISC_PIGSTEP = "minecraft:music_disc_pigstep";
public const MUSIC_DISC_RELIC = "minecraft:music_disc_relic";
public const MUSIC_DISC_STAL = "minecraft:music_disc_stal"; public const MUSIC_DISC_STAL = "minecraft:music_disc_stal";
public const MUSIC_DISC_STRAD = "minecraft:music_disc_strad"; public const MUSIC_DISC_STRAD = "minecraft:music_disc_strad";
public const MUSIC_DISC_WAIT = "minecraft:music_disc_wait"; public const MUSIC_DISC_WAIT = "minecraft:music_disc_wait";
@ -303,9 +337,11 @@ final class ItemTypeNames{
public const NETHERITE_SCRAP = "minecraft:netherite_scrap"; public const NETHERITE_SCRAP = "minecraft:netherite_scrap";
public const NETHERITE_SHOVEL = "minecraft:netherite_shovel"; public const NETHERITE_SHOVEL = "minecraft:netherite_shovel";
public const NETHERITE_SWORD = "minecraft:netherite_sword"; public const NETHERITE_SWORD = "minecraft:netherite_sword";
public const NETHERITE_UPGRADE_SMITHING_TEMPLATE = "minecraft:netherite_upgrade_smithing_template";
public const NPC_SPAWN_EGG = "minecraft:npc_spawn_egg"; public const NPC_SPAWN_EGG = "minecraft:npc_spawn_egg";
public const OAK_BOAT = "minecraft:oak_boat"; public const OAK_BOAT = "minecraft:oak_boat";
public const OAK_CHEST_BOAT = "minecraft:oak_chest_boat"; public const OAK_CHEST_BOAT = "minecraft:oak_chest_boat";
public const OAK_HANGING_SIGN = "minecraft:oak_hanging_sign";
public const OAK_SIGN = "minecraft:oak_sign"; public const OAK_SIGN = "minecraft:oak_sign";
public const OCELOT_SPAWN_EGG = "minecraft:ocelot_spawn_egg"; public const OCELOT_SPAWN_EGG = "minecraft:ocelot_spawn_egg";
public const ORANGE_DYE = "minecraft:orange_dye"; public const ORANGE_DYE = "minecraft:orange_dye";
@ -321,6 +357,8 @@ final class ItemTypeNames{
public const PIGLIN_SPAWN_EGG = "minecraft:piglin_spawn_egg"; public const PIGLIN_SPAWN_EGG = "minecraft:piglin_spawn_egg";
public const PILLAGER_SPAWN_EGG = "minecraft:pillager_spawn_egg"; public const PILLAGER_SPAWN_EGG = "minecraft:pillager_spawn_egg";
public const PINK_DYE = "minecraft:pink_dye"; public const PINK_DYE = "minecraft:pink_dye";
public const PITCHER_POD = "minecraft:pitcher_pod";
public const PLENTY_POTTERY_SHERD = "minecraft:plenty_pottery_sherd";
public const POISONOUS_POTATO = "minecraft:poisonous_potato"; public const POISONOUS_POTATO = "minecraft:poisonous_potato";
public const POLAR_BEAR_SPAWN_EGG = "minecraft:polar_bear_spawn_egg"; public const POLAR_BEAR_SPAWN_EGG = "minecraft:polar_bear_spawn_egg";
public const POPPED_CHORUS_FRUIT = "minecraft:popped_chorus_fruit"; public const POPPED_CHORUS_FRUIT = "minecraft:popped_chorus_fruit";
@ -330,7 +368,7 @@ final class ItemTypeNames{
public const POWDER_SNOW_BUCKET = "minecraft:powder_snow_bucket"; public const POWDER_SNOW_BUCKET = "minecraft:powder_snow_bucket";
public const PRISMARINE_CRYSTALS = "minecraft:prismarine_crystals"; public const PRISMARINE_CRYSTALS = "minecraft:prismarine_crystals";
public const PRISMARINE_SHARD = "minecraft:prismarine_shard"; public const PRISMARINE_SHARD = "minecraft:prismarine_shard";
public const PRIZE_POTTERY_SHARD = "minecraft:prize_pottery_shard"; public const PRIZE_POTTERY_SHERD = "minecraft:prize_pottery_sherd";
public const PUFFERFISH = "minecraft:pufferfish"; public const PUFFERFISH = "minecraft:pufferfish";
public const PUFFERFISH_BUCKET = "minecraft:pufferfish_bucket"; public const PUFFERFISH_BUCKET = "minecraft:pufferfish_bucket";
public const PUFFERFISH_SPAWN_EGG = "minecraft:pufferfish_spawn_egg"; public const PUFFERFISH_SPAWN_EGG = "minecraft:pufferfish_spawn_egg";
@ -343,6 +381,7 @@ final class ItemTypeNames{
public const RABBIT_HIDE = "minecraft:rabbit_hide"; public const RABBIT_HIDE = "minecraft:rabbit_hide";
public const RABBIT_SPAWN_EGG = "minecraft:rabbit_spawn_egg"; public const RABBIT_SPAWN_EGG = "minecraft:rabbit_spawn_egg";
public const RABBIT_STEW = "minecraft:rabbit_stew"; public const RABBIT_STEW = "minecraft:rabbit_stew";
public const RAISER_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:raiser_armor_trim_smithing_template";
public const RAPID_FERTILIZER = "minecraft:rapid_fertilizer"; public const RAPID_FERTILIZER = "minecraft:rapid_fertilizer";
public const RAVAGER_SPAWN_EGG = "minecraft:ravager_spawn_egg"; public const RAVAGER_SPAWN_EGG = "minecraft:ravager_spawn_egg";
public const RAW_COPPER = "minecraft:raw_copper"; public const RAW_COPPER = "minecraft:raw_copper";
@ -352,26 +391,34 @@ final class ItemTypeNames{
public const RED_DYE = "minecraft:red_dye"; public const RED_DYE = "minecraft:red_dye";
public const REDSTONE = "minecraft:redstone"; public const REDSTONE = "minecraft:redstone";
public const REPEATER = "minecraft:repeater"; public const REPEATER = "minecraft:repeater";
public const RIB_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:rib_armor_trim_smithing_template";
public const ROTTEN_FLESH = "minecraft:rotten_flesh"; public const ROTTEN_FLESH = "minecraft:rotten_flesh";
public const SADDLE = "minecraft:saddle"; public const SADDLE = "minecraft:saddle";
public const SALMON = "minecraft:salmon"; public const SALMON = "minecraft:salmon";
public const SALMON_BUCKET = "minecraft:salmon_bucket"; public const SALMON_BUCKET = "minecraft:salmon_bucket";
public const SALMON_SPAWN_EGG = "minecraft:salmon_spawn_egg"; public const SALMON_SPAWN_EGG = "minecraft:salmon_spawn_egg";
public const SCUTE = "minecraft:scute"; public const SCUTE = "minecraft:scute";
public const SENTRY_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:sentry_armor_trim_smithing_template";
public const SHAPER_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:shaper_armor_trim_smithing_template";
public const SHEAF_POTTERY_SHERD = "minecraft:sheaf_pottery_sherd";
public const SHEARS = "minecraft:shears"; public const SHEARS = "minecraft:shears";
public const SHEEP_SPAWN_EGG = "minecraft:sheep_spawn_egg"; public const SHEEP_SPAWN_EGG = "minecraft:sheep_spawn_egg";
public const SHELTER_POTTERY_SHERD = "minecraft:shelter_pottery_sherd";
public const SHIELD = "minecraft:shield"; public const SHIELD = "minecraft:shield";
public const SHULKER_SHELL = "minecraft:shulker_shell"; public const SHULKER_SHELL = "minecraft:shulker_shell";
public const SHULKER_SPAWN_EGG = "minecraft:shulker_spawn_egg"; public const SHULKER_SPAWN_EGG = "minecraft:shulker_spawn_egg";
public const SILENCE_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:silence_armor_trim_smithing_template";
public const SILVERFISH_SPAWN_EGG = "minecraft:silverfish_spawn_egg"; public const SILVERFISH_SPAWN_EGG = "minecraft:silverfish_spawn_egg";
public const SKELETON_HORSE_SPAWN_EGG = "minecraft:skeleton_horse_spawn_egg"; public const SKELETON_HORSE_SPAWN_EGG = "minecraft:skeleton_horse_spawn_egg";
public const SKELETON_SPAWN_EGG = "minecraft:skeleton_spawn_egg"; public const SKELETON_SPAWN_EGG = "minecraft:skeleton_spawn_egg";
public const SKULL = "minecraft:skull"; public const SKULL = "minecraft:skull";
public const SKULL_BANNER_PATTERN = "minecraft:skull_banner_pattern"; public const SKULL_BANNER_PATTERN = "minecraft:skull_banner_pattern";
public const SKULL_POTTERY_SHARD = "minecraft:skull_pottery_shard"; public const SKULL_POTTERY_SHERD = "minecraft:skull_pottery_sherd";
public const SLIME_BALL = "minecraft:slime_ball"; public const SLIME_BALL = "minecraft:slime_ball";
public const SLIME_SPAWN_EGG = "minecraft:slime_spawn_egg"; public const SLIME_SPAWN_EGG = "minecraft:slime_spawn_egg";
public const SNIFFER_SPAWN_EGG = "minecraft:sniffer_spawn_egg"; public const SNIFFER_SPAWN_EGG = "minecraft:sniffer_spawn_egg";
public const SNORT_POTTERY_SHERD = "minecraft:snort_pottery_sherd";
public const SNOUT_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:snout_armor_trim_smithing_template";
public const SNOW_GOLEM_SPAWN_EGG = "minecraft:snow_golem_spawn_egg"; public const SNOW_GOLEM_SPAWN_EGG = "minecraft:snow_golem_spawn_egg";
public const SNOWBALL = "minecraft:snowball"; public const SNOWBALL = "minecraft:snowball";
public const SOUL_CAMPFIRE = "minecraft:soul_campfire"; public const SOUL_CAMPFIRE = "minecraft:soul_campfire";
@ -379,10 +426,12 @@ final class ItemTypeNames{
public const SPAWN_EGG = "minecraft:spawn_egg"; public const SPAWN_EGG = "minecraft:spawn_egg";
public const SPIDER_EYE = "minecraft:spider_eye"; public const SPIDER_EYE = "minecraft:spider_eye";
public const SPIDER_SPAWN_EGG = "minecraft:spider_spawn_egg"; public const SPIDER_SPAWN_EGG = "minecraft:spider_spawn_egg";
public const SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:spire_armor_trim_smithing_template";
public const SPLASH_POTION = "minecraft:splash_potion"; public const SPLASH_POTION = "minecraft:splash_potion";
public const SPRUCE_BOAT = "minecraft:spruce_boat"; public const SPRUCE_BOAT = "minecraft:spruce_boat";
public const SPRUCE_CHEST_BOAT = "minecraft:spruce_chest_boat"; public const SPRUCE_CHEST_BOAT = "minecraft:spruce_chest_boat";
public const SPRUCE_DOOR = "minecraft:spruce_door"; public const SPRUCE_DOOR = "minecraft:spruce_door";
public const SPRUCE_HANGING_SIGN = "minecraft:spruce_hanging_sign";
public const SPRUCE_SIGN = "minecraft:spruce_sign"; public const SPRUCE_SIGN = "minecraft:spruce_sign";
public const SPYGLASS = "minecraft:spyglass"; public const SPYGLASS = "minecraft:spyglass";
public const SQUID_SPAWN_EGG = "minecraft:squid_spawn_egg"; public const SQUID_SPAWN_EGG = "minecraft:squid_spawn_egg";
@ -401,6 +450,7 @@ final class ItemTypeNames{
public const SWEET_BERRIES = "minecraft:sweet_berries"; public const SWEET_BERRIES = "minecraft:sweet_berries";
public const TADPOLE_BUCKET = "minecraft:tadpole_bucket"; public const TADPOLE_BUCKET = "minecraft:tadpole_bucket";
public const TADPOLE_SPAWN_EGG = "minecraft:tadpole_spawn_egg"; public const TADPOLE_SPAWN_EGG = "minecraft:tadpole_spawn_egg";
public const TIDE_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:tide_armor_trim_smithing_template";
public const TNT_MINECART = "minecraft:tnt_minecart"; public const TNT_MINECART = "minecraft:tnt_minecart";
public const TORCHFLOWER_SEEDS = "minecraft:torchflower_seeds"; public const TORCHFLOWER_SEEDS = "minecraft:torchflower_seeds";
public const TOTEM_OF_UNDYING = "minecraft:totem_of_undying"; public const TOTEM_OF_UNDYING = "minecraft:totem_of_undying";
@ -411,18 +461,23 @@ final class ItemTypeNames{
public const TROPICAL_FISH_SPAWN_EGG = "minecraft:tropical_fish_spawn_egg"; public const TROPICAL_FISH_SPAWN_EGG = "minecraft:tropical_fish_spawn_egg";
public const TURTLE_HELMET = "minecraft:turtle_helmet"; public const TURTLE_HELMET = "minecraft:turtle_helmet";
public const TURTLE_SPAWN_EGG = "minecraft:turtle_spawn_egg"; public const TURTLE_SPAWN_EGG = "minecraft:turtle_spawn_egg";
public const VEX_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:vex_armor_trim_smithing_template";
public const VEX_SPAWN_EGG = "minecraft:vex_spawn_egg"; public const VEX_SPAWN_EGG = "minecraft:vex_spawn_egg";
public const VILLAGER_SPAWN_EGG = "minecraft:villager_spawn_egg"; public const VILLAGER_SPAWN_EGG = "minecraft:villager_spawn_egg";
public const VINDICATOR_SPAWN_EGG = "minecraft:vindicator_spawn_egg"; public const VINDICATOR_SPAWN_EGG = "minecraft:vindicator_spawn_egg";
public const WANDERING_TRADER_SPAWN_EGG = "minecraft:wandering_trader_spawn_egg"; public const WANDERING_TRADER_SPAWN_EGG = "minecraft:wandering_trader_spawn_egg";
public const WARD_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:ward_armor_trim_smithing_template";
public const WARDEN_SPAWN_EGG = "minecraft:warden_spawn_egg"; public const WARDEN_SPAWN_EGG = "minecraft:warden_spawn_egg";
public const WARPED_DOOR = "minecraft:warped_door"; public const WARPED_DOOR = "minecraft:warped_door";
public const WARPED_FUNGUS_ON_A_STICK = "minecraft:warped_fungus_on_a_stick"; public const WARPED_FUNGUS_ON_A_STICK = "minecraft:warped_fungus_on_a_stick";
public const WARPED_HANGING_SIGN = "minecraft:warped_hanging_sign";
public const WARPED_SIGN = "minecraft:warped_sign"; public const WARPED_SIGN = "minecraft:warped_sign";
public const WATER_BUCKET = "minecraft:water_bucket"; public const WATER_BUCKET = "minecraft:water_bucket";
public const WAYFINDER_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:wayfinder_armor_trim_smithing_template";
public const WHEAT = "minecraft:wheat"; public const WHEAT = "minecraft:wheat";
public const WHEAT_SEEDS = "minecraft:wheat_seeds"; public const WHEAT_SEEDS = "minecraft:wheat_seeds";
public const WHITE_DYE = "minecraft:white_dye"; public const WHITE_DYE = "minecraft:white_dye";
public const WILD_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:wild_armor_trim_smithing_template";
public const WITCH_SPAWN_EGG = "minecraft:witch_spawn_egg"; public const WITCH_SPAWN_EGG = "minecraft:witch_spawn_egg";
public const WITHER_SKELETON_SPAWN_EGG = "minecraft:wither_skeleton_spawn_egg"; public const WITHER_SKELETON_SPAWN_EGG = "minecraft:wither_skeleton_spawn_egg";
public const WITHER_SPAWN_EGG = "minecraft:wither_spawn_egg"; public const WITHER_SPAWN_EGG = "minecraft:wither_spawn_egg";

View File

@ -36,6 +36,11 @@ interface RuntimeDataDescriber extends RuntimeEnumDescriber{
public function horizontalFacing(int &$facing) : void; public function horizontalFacing(int &$facing) : void;
/**
* @param int[] $faces
*/
public function facingFlags(array &$faces) : void;
/** /**
* @param int[] $faces * @param int[] $faces
*/ */

View File

@ -86,6 +86,20 @@ final class RuntimeDataReader implements RuntimeDataDescriber{
}; };
} }
/**
* @param int[] $faces
*/
public function facingFlags(array &$faces) : void{
$result = [];
foreach(Facing::ALL as $facing){
if($this->readBool()){
$result[$facing] = $facing;
}
}
$faces = $result;
}
/** /**
* @param int[] $faces * @param int[] $faces
*/ */

View File

@ -56,6 +56,10 @@ final class RuntimeDataSizeCalculator implements RuntimeDataDescriber{
$this->addBits(2); $this->addBits(2);
} }
public function facingFlags(array &$faces) : void{
$this->addBits(count(Facing::ALL));
}
public function horizontalFacingFlags(array &$faces) : void{ public function horizontalFacingFlags(array &$faces) : void{
$this->addBits(count(Facing::HORIZONTAL)); $this->addBits(count(Facing::HORIZONTAL));
} }

View File

@ -85,6 +85,16 @@ final class RuntimeDataWriter implements RuntimeDataDescriber{
}); });
} }
/**
* @param int[] $faces
*/
public function facingFlags(array &$faces) : void{
$uniqueFaces = array_flip($faces);
foreach(Facing::ALL as $facing){
$this->writeBool(isset($uniqueFaces[$facing]));
}
}
/** /**
* @param int[] $faces * @param int[] $faces
*/ */

View File

@ -130,10 +130,11 @@ trait RuntimeEnumDeserializerTrait{
$value = match($this->readInt(3)){ $value = match($this->readInt(3)){
0 => \pocketmine\block\utils\MobHeadType::CREEPER(), 0 => \pocketmine\block\utils\MobHeadType::CREEPER(),
1 => \pocketmine\block\utils\MobHeadType::DRAGON(), 1 => \pocketmine\block\utils\MobHeadType::DRAGON(),
2 => \pocketmine\block\utils\MobHeadType::PLAYER(), 2 => \pocketmine\block\utils\MobHeadType::PIGLIN(),
3 => \pocketmine\block\utils\MobHeadType::SKELETON(), 3 => \pocketmine\block\utils\MobHeadType::PLAYER(),
4 => \pocketmine\block\utils\MobHeadType::WITHER_SKELETON(), 4 => \pocketmine\block\utils\MobHeadType::SKELETON(),
5 => \pocketmine\block\utils\MobHeadType::ZOMBIE(), 5 => \pocketmine\block\utils\MobHeadType::WITHER_SKELETON(),
6 => \pocketmine\block\utils\MobHeadType::ZOMBIE(),
default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for MobHeadType") default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for MobHeadType")
}; };
} }

View File

@ -130,10 +130,11 @@ trait RuntimeEnumSerializerTrait{
$this->writeInt(3, match($value){ $this->writeInt(3, match($value){
\pocketmine\block\utils\MobHeadType::CREEPER() => 0, \pocketmine\block\utils\MobHeadType::CREEPER() => 0,
\pocketmine\block\utils\MobHeadType::DRAGON() => 1, \pocketmine\block\utils\MobHeadType::DRAGON() => 1,
\pocketmine\block\utils\MobHeadType::PLAYER() => 2, \pocketmine\block\utils\MobHeadType::PIGLIN() => 2,
\pocketmine\block\utils\MobHeadType::SKELETON() => 3, \pocketmine\block\utils\MobHeadType::PLAYER() => 3,
\pocketmine\block\utils\MobHeadType::WITHER_SKELETON() => 4, \pocketmine\block\utils\MobHeadType::SKELETON() => 4,
\pocketmine\block\utils\MobHeadType::ZOMBIE() => 5, \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") default => throw new \pocketmine\utils\AssumptionFailedError("All MobHeadType cases should be covered")
}); });
} }

View File

@ -302,8 +302,9 @@ final class ItemTypeIds{
public const MEDICINE = 20263; public const MEDICINE = 20263;
public const MANGROVE_BOAT = 20264; public const MANGROVE_BOAT = 20264;
public const GLOW_BERRIES = 20265; public const GLOW_BERRIES = 20265;
public const CHERRY_SIGN = 20266;
public const FIRST_UNUSED_ITEM_ID = 20266; public const FIRST_UNUSED_ITEM_ID = 20267;
private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID; private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID;

View File

@ -208,6 +208,19 @@ final class StringToItemParser extends StringToTParser{
$result->registerBlock("cauldron", fn() => Blocks::CAULDRON()); $result->registerBlock("cauldron", fn() => Blocks::CAULDRON());
$result->registerBlock("cave_vines", fn() => Blocks::CAVE_VINES()); $result->registerBlock("cave_vines", fn() => Blocks::CAVE_VINES());
$result->registerBlock("chain", fn() => Blocks::CHAIN()); $result->registerBlock("chain", fn() => Blocks::CHAIN());
$result->registerBlock("cherry_button", fn() => Blocks::CHERRY_BUTTON());
$result->registerBlock("cherry_door", fn() => Blocks::CHERRY_DOOR());
$result->registerBlock("cherry_fence", fn() => Blocks::CHERRY_FENCE());
$result->registerBlock("cherry_fence_gate", fn() => Blocks::CHERRY_FENCE_GATE());
$result->registerBlock("cherry_leaves", fn() => Blocks::CHERRY_LEAVES());
$result->registerBlock("cherry_log", fn() => Blocks::CHERRY_LOG());
$result->registerBlock("cherry_planks", fn() => Blocks::CHERRY_PLANKS());
$result->registerBlock("cherry_pressure_plate", fn() => Blocks::CHERRY_PRESSURE_PLATE());
$result->registerBlock("cherry_sign", fn() => Blocks::CHERRY_SIGN());
$result->registerBlock("cherry_slab", fn() => Blocks::CHERRY_SLAB());
$result->registerBlock("cherry_stairs", fn() => Blocks::CHERRY_STAIRS());
$result->registerBlock("cherry_trapdoor", fn() => Blocks::CHERRY_TRAPDOOR());
$result->registerBlock("cherry_wood", fn() => Blocks::CHERRY_WOOD());
$result->registerBlock("chemical_heat", fn() => Blocks::CHEMICAL_HEAT()); $result->registerBlock("chemical_heat", fn() => Blocks::CHEMICAL_HEAT());
$result->registerBlock("chemistry_table", fn() => Blocks::COMPOUND_CREATOR()); $result->registerBlock("chemistry_table", fn() => Blocks::COMPOUND_CREATOR());
$result->registerBlock("chest", fn() => Blocks::CHEST()); $result->registerBlock("chest", fn() => Blocks::CHEST());
@ -647,6 +660,7 @@ final class StringToItemParser extends StringToTParser{
$result->registerBlock("glowingobsidian", fn() => Blocks::GLOWING_OBSIDIAN()); $result->registerBlock("glowingobsidian", fn() => Blocks::GLOWING_OBSIDIAN());
$result->registerBlock("glowstone", fn() => Blocks::GLOWSTONE()); $result->registerBlock("glowstone", fn() => Blocks::GLOWSTONE());
$result->registerBlock("glowstone_block", fn() => Blocks::GLOWSTONE()); $result->registerBlock("glowstone_block", fn() => Blocks::GLOWSTONE());
$result->registerBlock("glow_lichen", fn() => Blocks::GLOW_LICHEN());
$result->registerBlock("gold", fn() => Blocks::GOLD()); $result->registerBlock("gold", fn() => Blocks::GOLD());
$result->registerBlock("gold_block", fn() => Blocks::GOLD()); $result->registerBlock("gold_block", fn() => Blocks::GOLD());
$result->registerBlock("gold_ore", fn() => Blocks::GOLD_ORE()); $result->registerBlock("gold_ore", fn() => Blocks::GOLD_ORE());
@ -842,6 +856,7 @@ final class StringToItemParser extends StringToTParser{
$result->registerBlock("packed_mud", fn() => Blocks::PACKED_MUD()); $result->registerBlock("packed_mud", fn() => Blocks::PACKED_MUD());
$result->registerBlock("peony", fn() => Blocks::PEONY()); $result->registerBlock("peony", fn() => Blocks::PEONY());
$result->registerBlock("pink_tulip", fn() => Blocks::PINK_TULIP()); $result->registerBlock("pink_tulip", fn() => Blocks::PINK_TULIP());
$result->registerBlock("piglin_head", fn() => Blocks::MOB_HEAD()->setMobHeadType(MobHeadType::PIGLIN()));
$result->registerBlock("plank", fn() => Blocks::OAK_PLANKS()); $result->registerBlock("plank", fn() => Blocks::OAK_PLANKS());
$result->registerBlock("planks", 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()));
@ -1064,6 +1079,7 @@ final class StringToItemParser extends StringToTParser{
$result->registerBlock("trunk2", fn() => Blocks::ACACIA_LOG()->setStripped(false)); $result->registerBlock("trunk2", fn() => Blocks::ACACIA_LOG()->setStripped(false));
$result->registerBlock("tuff", fn() => Blocks::TUFF()); $result->registerBlock("tuff", fn() => Blocks::TUFF());
$result->registerBlock("twisting_vines", fn() => Blocks::TWISTING_VINES()); $result->registerBlock("twisting_vines", fn() => Blocks::TWISTING_VINES());
$result->registerBlock("underwater_tnt", fn() => Blocks::TNT()->setWorksUnderwater(true));
$result->registerBlock("underwater_torch", fn() => Blocks::UNDERWATER_TORCH()); $result->registerBlock("underwater_torch", fn() => Blocks::UNDERWATER_TORCH());
$result->registerBlock("undyed_shulker_box", fn() => Blocks::SHULKER_BOX()); $result->registerBlock("undyed_shulker_box", fn() => Blocks::SHULKER_BOX());
$result->registerBlock("unlit_redstone_torch", fn() => Blocks::REDSTONE_TORCH()); $result->registerBlock("unlit_redstone_torch", fn() => Blocks::REDSTONE_TORCH());

View File

@ -114,6 +114,7 @@ use pocketmine\world\World;
* @method static Item CHEMICAL_SULPHATE() * @method static Item CHEMICAL_SULPHATE()
* @method static Item CHEMICAL_TUNGSTEN_CHLORIDE() * @method static Item CHEMICAL_TUNGSTEN_CHLORIDE()
* @method static Item CHEMICAL_WATER() * @method static Item CHEMICAL_WATER()
* @method static ItemBlockWallOrFloor CHERRY_SIGN()
* @method static ChorusFruit CHORUS_FRUIT() * @method static ChorusFruit CHORUS_FRUIT()
* @method static Item CLAY() * @method static Item CLAY()
* @method static Clock CLOCK() * @method static Clock CLOCK()
@ -362,6 +363,7 @@ final class VanillaItems{
self::register("bucket", new Bucket(new IID(Ids::BUCKET), "Bucket")); self::register("bucket", new Bucket(new IID(Ids::BUCKET), "Bucket"));
self::register("carrot", new Carrot(new IID(Ids::CARROT), "Carrot")); self::register("carrot", new Carrot(new IID(Ids::CARROT), "Carrot"));
self::register("charcoal", new Coal(new IID(Ids::CHARCOAL), "Charcoal")); self::register("charcoal", new Coal(new IID(Ids::CHARCOAL), "Charcoal"));
self::register("cherry_sign", new ItemBlockWallOrFloor(new IID(Ids::CHERRY_SIGN), Blocks::CHERRY_SIGN(), Blocks::CHERRY_WALL_SIGN()));
self::register("chemical_aluminium_oxide", new Item(new IID(Ids::CHEMICAL_ALUMINIUM_OXIDE), "Aluminium Oxide")); self::register("chemical_aluminium_oxide", new Item(new IID(Ids::CHEMICAL_ALUMINIUM_OXIDE), "Aluminium Oxide"));
self::register("chemical_ammonia", new Item(new IID(Ids::CHEMICAL_AMMONIA), "Ammonia")); self::register("chemical_ammonia", new Item(new IID(Ids::CHEMICAL_AMMONIA), "Ammonia"));
self::register("chemical_barium_sulphate", new Item(new IID(Ids::CHEMICAL_BARIUM_SULPHATE), "Barium Sulphate")); self::register("chemical_barium_sulphate", new Item(new IID(Ids::CHEMICAL_BARIUM_SULPHATE), "Barium Sulphate"));

View File

@ -39,7 +39,6 @@ use pocketmine\world\format\io\FastChunkSerializer;
class ChunkRequestTask extends AsyncTask{ class ChunkRequestTask extends AsyncTask{
private const TLS_KEY_PROMISE = "promise"; private const TLS_KEY_PROMISE = "promise";
private const TLS_KEY_ERROR_HOOK = "errorHook";
protected string $chunk; protected string $chunk;
protected int $chunkX; protected int $chunkX;
@ -48,10 +47,7 @@ class ChunkRequestTask extends AsyncTask{
protected NonThreadSafeValue $compressor; protected NonThreadSafeValue $compressor;
private string $tiles; private string $tiles;
/** public function __construct(int $chunkX, int $chunkZ, Chunk $chunk, CompressBatchPromise $promise, Compressor $compressor){
* @phpstan-param (\Closure() : void)|null $onError
*/
public function __construct(int $chunkX, int $chunkZ, Chunk $chunk, CompressBatchPromise $promise, Compressor $compressor, ?\Closure $onError = null){
$this->compressor = new NonThreadSafeValue($compressor); $this->compressor = new NonThreadSafeValue($compressor);
$this->chunk = FastChunkSerializer::serializeTerrain($chunk); $this->chunk = FastChunkSerializer::serializeTerrain($chunk);
@ -60,7 +56,6 @@ class ChunkRequestTask extends AsyncTask{
$this->tiles = ChunkSerializer::serializeTiles($chunk); $this->tiles = ChunkSerializer::serializeTiles($chunk);
$this->storeLocal(self::TLS_KEY_PROMISE, $promise); $this->storeLocal(self::TLS_KEY_PROMISE, $promise);
$this->storeLocal(self::TLS_KEY_ERROR_HOOK, $onError);
} }
public function onRun() : void{ public function onRun() : void{
@ -75,17 +70,6 @@ class ChunkRequestTask extends AsyncTask{
$this->setResult($this->compressor->deserialize()->compress($stream->getBuffer())); $this->setResult($this->compressor->deserialize()->compress($stream->getBuffer()));
} }
public function onError() : void{
/**
* @var \Closure|null $hook
* @phpstan-var (\Closure() : void)|null $hook
*/
$hook = $this->fetchLocal(self::TLS_KEY_ERROR_HOOK);
if($hook !== null){
$hook();
}
}
public function onCompletion() : void{ public function onCompletion() : void{
/** @var CompressBatchPromise $promise */ /** @var CompressBatchPromise $promise */
$promise = $this->fetchLocal(self::TLS_KEY_PROMISE); $promise = $this->fetchLocal(self::TLS_KEY_PROMISE);

View File

@ -51,7 +51,7 @@ use const SORT_NUMERIC;
final class StandardEntityEventBroadcaster implements EntityEventBroadcaster{ final class StandardEntityEventBroadcaster implements EntityEventBroadcaster{
public function __construct( public function __construct(
private StandardPacketBroadcaster $broadcaster, private PacketBroadcaster $broadcaster,
private TypeConverter $typeConverter private TypeConverter $typeConverter
){} ){}

View File

@ -118,14 +118,7 @@ class ChunkCache implements ChunkListener{
$chunkZ, $chunkZ,
$chunk, $chunk,
$this->caches[$chunkHash], $this->caches[$chunkHash],
$this->compressor, $this->compressor
function() use ($chunkHash, $chunkX, $chunkZ) : void{
$this->world->getLogger()->error("Failed preparing chunk $chunkX $chunkZ, retrying");
if(isset($this->caches[$chunkHash])){
$this->restartPendingRequest($chunkX, $chunkZ);
}
}
) )
); );

View File

@ -24,15 +24,17 @@ declare(strict_types=1);
namespace pocketmine\scheduler; namespace pocketmine\scheduler;
use pmmp\thread\Thread as NativeThread; use pmmp\thread\Thread as NativeThread;
use pmmp\thread\ThreadSafeArray;
use pocketmine\snooze\SleeperHandler; use pocketmine\snooze\SleeperHandler;
use pocketmine\thread\log\ThreadSafeLogger; use pocketmine\thread\log\ThreadSafeLogger;
use pocketmine\thread\ThreadSafeClassLoader; use pocketmine\thread\ThreadSafeClassLoader;
use pocketmine\timings\Timings;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Utils; use pocketmine\utils\Utils;
use function array_keys; use function array_keys;
use function array_map; use function array_map;
use function assert; use function assert;
use function count; use function count;
use function get_class;
use function spl_object_id; use function spl_object_id;
use function time; use function time;
use const PHP_INT_MAX; use const PHP_INT_MAX;
@ -130,6 +132,8 @@ class AsyncPool{
foreach($this->workerStartHooks as $hook){ foreach($this->workerStartHooks as $hook){
$hook($workerId); $hook($workerId);
} }
}else{
$this->checkCrashedWorker($workerId, null);
} }
return $this->workers[$workerId]; return $this->workers[$workerId];
@ -146,7 +150,6 @@ class AsyncPool{
throw new \InvalidArgumentException("Cannot submit the same AsyncTask instance more than once"); throw new \InvalidArgumentException("Cannot submit the same AsyncTask instance more than once");
} }
$task->progressUpdates = new ThreadSafeArray();
$task->setSubmitted(); $task->setSubmitted();
$this->getWorker($worker)->submit($task); $this->getWorker($worker)->submit($task);
@ -199,6 +202,28 @@ class AsyncPool{
return $worker; return $worker;
} }
private function checkCrashedWorker(int $workerId, ?AsyncTask $crashedTask) : void{
$entry = $this->workers[$workerId];
if($entry->worker->isTerminated()){
if($crashedTask === null){
foreach($entry->tasks as $task){
if($task->isTerminated()){
$crashedTask = $task;
break;
}elseif(!$task->isFinished()){
break;
}
}
}
if($crashedTask !== null){
$message = "Worker $workerId crashed while running task " . get_class($crashedTask) . "#" . spl_object_id($crashedTask);
}else{
$message = "Worker $workerId crashed for unknown reason";
}
throw new \RuntimeException($message);
}
}
/** /**
* Collects finished and/or crashed tasks from the workers, firing their on-completion hooks where appropriate. * Collects finished and/or crashed tasks from the workers, firing their on-completion hooks where appropriate.
* *
@ -231,9 +256,9 @@ class AsyncPool{
if($task->isFinished()){ //make sure the task actually executed before trying to collect if($task->isFinished()){ //make sure the task actually executed before trying to collect
$queue->dequeue(); $queue->dequeue();
if($task->isCrashed()){ if($task->isTerminated()){
$this->logger->critical("Could not execute asynchronous task " . (new \ReflectionClass($task))->getShortName() . ": Task crashed"); $this->checkCrashedWorker($worker, $task);
$task->onError(); throw new AssumptionFailedError("checkCrashedWorker() should have thrown an exception, making this unreachable");
}elseif(!$task->hasCancelledRun()){ }elseif(!$task->hasCancelledRun()){
/* /*
* It's possible for a task to submit a progress update and then finish before the progress * It's possible for a task to submit a progress update and then finish before the progress
@ -244,11 +269,13 @@ class AsyncPool{
* lost. Thus, it's necessary to do one last check here to make sure all progress updates have * lost. Thus, it's necessary to do one last check here to make sure all progress updates have
* been consumed before completing. * been consumed before completing.
*/ */
$task->checkProgressUpdates(); $this->checkTaskProgressUpdates($task);
$task->onCompletion(); Timings::getAsyncTaskCompletionTimings($task)->time(function() use ($task) : void{
$task->onCompletion();
});
} }
}else{ }else{
$task->checkProgressUpdates(); $this->checkTaskProgressUpdates($task);
$more = true; $more = true;
break; //current task is still running, skip to next worker break; //current task is still running, skip to next worker
} }
@ -296,4 +323,10 @@ class AsyncPool{
} }
$this->workers = []; $this->workers = [];
} }
private function checkTaskProgressUpdates(AsyncTask $task) : void{
Timings::getAsyncTaskProgressUpdateTimings($task)->time(function() use ($task) : void{
$task->checkProgressUpdates();
});
}
} }

View File

@ -61,34 +61,27 @@ use function spl_object_id;
*/ */
abstract class AsyncTask extends Runnable{ abstract class AsyncTask extends Runnable{
/** /**
* @var \ArrayObject|mixed[]|null object hash => mixed data * @var mixed[][] object hash => mixed data
* @phpstan-var \ArrayObject<int, array<string, mixed>>|null * @phpstan-var array<int, array<string, mixed>>
* *
* Used to store objects which are only needed on one thread and should not be serialized. * Used to store thread-local data to be used by onCompletion().
*/ */
private static ?\ArrayObject $threadLocalStorage = null; private static array $threadLocalStorage = [];
/** @phpstan-var ThreadSafeArray<int, string> */ /** @phpstan-var ThreadSafeArray<int, string>|null */
public ThreadSafeArray $progressUpdates; private ?ThreadSafeArray $progressUpdates = null;
private ThreadSafe|string|int|bool|null|float $result = null; private ThreadSafe|string|int|bool|null|float $result = null;
private bool $cancelRun = false; private bool $cancelRun = false;
private bool $submitted = false; private bool $submitted = false;
private bool $crashed = false;
private bool $finished = false; private bool $finished = false;
public function run() : void{ public function run() : void{
$this->result = null; $this->result = null;
if(!$this->cancelRun){ if(!$this->cancelRun){
try{ $this->onRun();
$this->onRun();
}catch(\Throwable $e){
$this->crashed = true;
\GlobalLogger::get()->logException($e);
}
} }
$this->finished = true; $this->finished = true;
@ -97,8 +90,11 @@ abstract class AsyncTask extends Runnable{
$worker->getNotifier()->wakeupSleeper(); $worker->getNotifier()->wakeupSleeper();
} }
/**
* @deprecated
*/
public function isCrashed() : bool{ public function isCrashed() : bool{
return $this->crashed || $this->isTerminated(); return $this->isTerminated();
} }
/** /**
@ -106,7 +102,7 @@ abstract class AsyncTask extends Runnable{
* because it is not true prior to task execution. * because it is not true prior to task execution.
*/ */
public function isFinished() : bool{ public function isFinished() : bool{
return $this->finished || $this->isCrashed(); return $this->finished || $this->isTerminated();
} }
public function hasResult() : bool{ public function hasResult() : bool{
@ -163,15 +159,22 @@ abstract class AsyncTask extends Runnable{
* @param mixed $progress A value that can be safely serialize()'ed. * @param mixed $progress A value that can be safely serialize()'ed.
*/ */
public function publishProgress(mixed $progress) : void{ public function publishProgress(mixed $progress) : void{
$this->progressUpdates[] = igbinary_serialize($progress) ?? throw new \InvalidArgumentException("Progress must be serializable"); $progressUpdates = $this->progressUpdates;
if($progressUpdates === null){
$progressUpdates = $this->progressUpdates = new ThreadSafeArray();
}
$progressUpdates[] = igbinary_serialize($progress) ?? throw new \InvalidArgumentException("Progress must be serializable");
} }
/** /**
* @internal Only call from AsyncPool.php on the main thread * @internal Only call from AsyncPool.php on the main thread
*/ */
public function checkProgressUpdates() : void{ public function checkProgressUpdates() : void{
while(($progress = $this->progressUpdates->shift()) !== null){ $progressUpdates = $this->progressUpdates;
$this->onProgressUpdate(igbinary_unserialize($progress)); if($progressUpdates !== null){
while(($progress = $progressUpdates->shift()) !== null){
$this->onProgressUpdate(igbinary_unserialize($progress));
}
} }
} }
@ -188,8 +191,7 @@ abstract class AsyncTask extends Runnable{
} }
/** /**
* Called from the main thread when the async task experiences an error during onRun(). Use this for things like * @deprecated No longer used
* promise rejection.
*/ */
public function onError() : void{ public function onError() : void{
@ -210,15 +212,6 @@ abstract class AsyncTask extends Runnable{
* from. * from.
*/ */
protected function storeLocal(string $key, mixed $complexData) : void{ protected function storeLocal(string $key, mixed $complexData) : void{
if(self::$threadLocalStorage === null){
/*
* It's necessary to use an object (not array) here because pthreads is stupid. Non-default array statics
* will be inherited when task classes are copied to the worker thread, which would cause unwanted
* inheritance of primitive thread-locals, which we really don't want for various reasons.
* It won't try to inherit objects though, so this is the easiest solution.
*/
self::$threadLocalStorage = new \ArrayObject();
}
self::$threadLocalStorage[spl_object_id($this)][$key] = $complexData; self::$threadLocalStorage[spl_object_id($this)][$key] = $complexData;
} }
@ -234,7 +227,7 @@ abstract class AsyncTask extends Runnable{
*/ */
protected function fetchLocal(string $key){ protected function fetchLocal(string $key){
$id = spl_object_id($this); $id = spl_object_id($this);
if(self::$threadLocalStorage === null || !isset(self::$threadLocalStorage[$id][$key])){ if(!isset(self::$threadLocalStorage[$id][$key])){
throw new \InvalidArgumentException("No matching thread-local data found on this thread"); throw new \InvalidArgumentException("No matching thread-local data found on this thread");
} }
@ -243,18 +236,7 @@ abstract class AsyncTask extends Runnable{
final public function __destruct(){ final public function __destruct(){
$this->reallyDestruct(); $this->reallyDestruct();
if(self::$threadLocalStorage !== null && isset(self::$threadLocalStorage[$h = spl_object_id($this)])){ unset(self::$threadLocalStorage[spl_object_id($this)]);
//Beware changing this code!
//This code may cause the GC to be triggered, causing destruction of other AsyncTasks (which may or may not
//have been indirectly referenced by the TLS).
//This may cause the code to be re-entered from a different context unexpectedly, causing a crash if handled
//incorrectly.
if(self::$threadLocalStorage->count() === 1){
self::$threadLocalStorage = null;
}else{
unset(self::$threadLocalStorage[$h]);
}
}
} }
/** /**

View File

@ -31,6 +31,7 @@ use pocketmine\thread\Worker;
use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\AssumptionFailedError;
use function gc_enable; use function gc_enable;
use function ini_set; use function ini_set;
use function set_exception_handler;
class AsyncWorker extends Worker{ class AsyncWorker extends Worker{
/** @var mixed[] */ /** @var mixed[] */
@ -67,6 +68,10 @@ class AsyncWorker extends Worker{
} }
$this->saveToThreadStore(self::TLS_KEY_NOTIFIER, $this->sleeperEntry->createNotifier()); $this->saveToThreadStore(self::TLS_KEY_NOTIFIER, $this->sleeperEntry->createNotifier());
set_exception_handler(function(\Throwable $e){
$this->logger->logException($e);
});
} }
public function getLogger() : ThreadSafeLogger{ public function getLogger() : ThreadSafeLogger{

View File

@ -29,6 +29,7 @@ use pocketmine\event\Event;
use pocketmine\network\mcpe\protocol\ClientboundPacket; use pocketmine\network\mcpe\protocol\ClientboundPacket;
use pocketmine\network\mcpe\protocol\ServerboundPacket; use pocketmine\network\mcpe\protocol\ServerboundPacket;
use pocketmine\player\Player; use pocketmine\player\Player;
use pocketmine\scheduler\AsyncTask;
use pocketmine\scheduler\TaskHandler; use pocketmine\scheduler\TaskHandler;
use function get_class; use function get_class;
use function str_starts_with; use function str_starts_with;
@ -115,6 +116,17 @@ abstract class Timings{
/** @var TimingsHandler[][] */ /** @var TimingsHandler[][] */
private static array $eventHandlers = []; private static array $eventHandlers = [];
private static TimingsHandler $asyncTaskProgressUpdateParent;
private static TimingsHandler $asyncTaskCompletionParent;
private static TimingsHandler $asyncTaskErrorParent;
/** @var TimingsHandler[] */
private static array $asyncTaskProgressUpdate = [];
/** @var TimingsHandler[] */
private static array $asyncTaskCompletion = [];
/** @var TimingsHandler[] */
private static array $asyncTaskError = [];
public static function init() : void{ public static function init() : void{
if(self::$initialized){ if(self::$initialized){
return; return;
@ -168,7 +180,11 @@ abstract class Timings{
self::$itemEntityBaseTick = new TimingsHandler("Entity Base Tick - ItemEntity", group: self::GROUP_BREAKDOWN); self::$itemEntityBaseTick = new TimingsHandler("Entity Base Tick - ItemEntity", group: self::GROUP_BREAKDOWN);
self::$schedulerSync = new TimingsHandler("Scheduler - Sync Tasks", group: self::GROUP_BREAKDOWN); self::$schedulerSync = new TimingsHandler("Scheduler - Sync Tasks", group: self::GROUP_BREAKDOWN);
self::$schedulerAsync = new TimingsHandler("Scheduler - Async Tasks", group: self::GROUP_BREAKDOWN); self::$schedulerAsync = new TimingsHandler("Scheduler - Async Tasks", group: self::GROUP_BREAKDOWN);
self::$asyncTaskProgressUpdateParent = new TimingsHandler("Async Tasks - Progress Updates", self::$schedulerAsync, group: self::GROUP_BREAKDOWN);
self::$asyncTaskCompletionParent = new TimingsHandler("Async Tasks - Completion Handlers", self::$schedulerAsync, group: self::GROUP_BREAKDOWN);
self::$asyncTaskErrorParent = new TimingsHandler("Async Tasks - Error Handlers", self::$schedulerAsync, group: self::GROUP_BREAKDOWN);
self::$playerCommand = new TimingsHandler("Player Command", group: self::GROUP_BREAKDOWN); self::$playerCommand = new TimingsHandler("Player Command", group: self::GROUP_BREAKDOWN);
self::$craftingDataCacheRebuild = new TimingsHandler("Build CraftingDataPacket Cache", group: self::GROUP_BREAKDOWN); self::$craftingDataCacheRebuild = new TimingsHandler("Build CraftingDataPacket Cache", group: self::GROUP_BREAKDOWN);
@ -299,4 +315,46 @@ abstract class Timings{
return self::$eventHandlers[$event][$handlerName]; return self::$eventHandlers[$event][$handlerName];
} }
public static function getAsyncTaskProgressUpdateTimings(AsyncTask $task, string $group = self::GROUP_BREAKDOWN) : TimingsHandler{
$taskClass = $task::class;
if(!isset(self::$asyncTaskProgressUpdate[$taskClass])){
self::init();
self::$asyncTaskProgressUpdate[$taskClass] = new TimingsHandler(
"AsyncTask - " . self::shortenCoreClassName($taskClass, "pocketmine\\") . " - Progress Updates",
self::$asyncTaskProgressUpdateParent,
$group
);
}
return self::$asyncTaskProgressUpdate[$taskClass];
}
public static function getAsyncTaskCompletionTimings(AsyncTask $task, string $group = self::GROUP_BREAKDOWN) : TimingsHandler{
$taskClass = $task::class;
if(!isset(self::$asyncTaskCompletion[$taskClass])){
self::init();
self::$asyncTaskCompletion[$taskClass] = new TimingsHandler(
"AsyncTask - " . self::shortenCoreClassName($taskClass, "pocketmine\\") . " - Completion Handler",
self::$asyncTaskCompletionParent,
$group
);
}
return self::$asyncTaskCompletion[$taskClass];
}
public static function getAsyncTaskErrorTimings(AsyncTask $task, string $group = self::GROUP_BREAKDOWN) : TimingsHandler{
$taskClass = $task::class;
if(!isset(self::$asyncTaskError[$taskClass])){
self::init();
self::$asyncTaskError[$taskClass] = new TimingsHandler(
"AsyncTask - " . self::shortenCoreClassName($taskClass, "pocketmine\\") . " - Error Handler",
self::$asyncTaskErrorParent,
$group
);
}
return self::$asyncTaskError[$taskClass];
}
} }

View File

@ -70,7 +70,7 @@ final class GlobalBlockStateHandlers{
BlockIdMetaUpgrader::loadFromString( BlockIdMetaUpgrader::loadFromString(
Filesystem::fileGetContents(Path::join( Filesystem::fileGetContents(Path::join(
BEDROCK_BLOCK_UPGRADE_SCHEMA_PATH, BEDROCK_BLOCK_UPGRADE_SCHEMA_PATH,
'1.12.0_to_1.18.10_blockstate_map.bin' 'id_meta_to_nbt/1.12.0.bin'
)), )),
LegacyBlockIdToStringIdMap::getInstance(), LegacyBlockIdToStringIdMap::getInstance(),
$blockStateUpgrader $blockStateUpgrader

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use pocketmine\utils\Utils;
use function array_unique; use function array_unique;
use function max; use function max;
@ -43,4 +44,14 @@ class BlockTypeIdsTest extends TestCase{
self::assertSameSize($idTable, array_unique($idTable), "Every BlockTypeID must be unique"); self::assertSameSize($idTable, array_unique($idTable), "Every BlockTypeID must be unique");
} }
public function testVanillaBlocksParity() : void{
$reflect = new \ReflectionClass(BlockTypeIds::class);
foreach(Utils::stringifyKeys(VanillaBlocks::getAll()) as $name => $block){
$expected = $block->getTypeId();
$actual = $reflect->getConstant($name);
self::assertSame($expected, $actual, "VanillaBlocks::$name() does not match BlockTypeIds::$name");
}
}
} }

File diff suppressed because one or more lines are too long

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\item; namespace pocketmine\item;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use pocketmine\utils\Utils;
use function array_unique; use function array_unique;
use function max; use function max;
@ -43,4 +44,17 @@ class ItemTypeIdsTest extends TestCase{
self::assertSameSize($idTable, array_unique($idTable), "Every ItemTypeID must be unique"); self::assertSameSize($idTable, array_unique($idTable), "Every ItemTypeID must be unique");
} }
public function testVanillaItemsParity() : void{
$reflect = new \ReflectionClass(ItemTypeIds::class);
foreach(Utils::stringifyKeys(VanillaItems::getAll()) as $name => $item){
if($item instanceof ItemBlock){
continue;
}
$expected = $item->getTypeId();
$actual = $reflect->getConstant($name);
self::assertSame($expected, $actual, "VanillaItems::$name() type ID does not match ItemTypeIds::$name");
}
}
} }

View File

@ -26,20 +26,29 @@ namespace pocketmine\tools\generate_blockstate_upgrade_schema;
use pocketmine\data\bedrock\block\BlockStateData; use pocketmine\data\bedrock\block\BlockStateData;
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchema; use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchema;
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaBlockRemap; use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaBlockRemap;
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaFlattenedName;
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaUtils; use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaUtils;
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaValueRemap; use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaValueRemap;
use pocketmine\nbt\LittleEndianNbtSerializer;
use pocketmine\nbt\tag\StringTag;
use pocketmine\nbt\tag\Tag; use pocketmine\nbt\tag\Tag;
use pocketmine\nbt\TreeRoot;
use pocketmine\network\mcpe\protocol\serializer\NetworkNbtSerializer; use pocketmine\network\mcpe\protocol\serializer\NetworkNbtSerializer;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Filesystem; use pocketmine\utils\Filesystem;
use pocketmine\utils\Utils; use pocketmine\utils\Utils;
use function array_filter;
use function array_key_first; use function array_key_first;
use function array_merge; use function array_keys;
use function array_map;
use function array_shift;
use function array_values; use function array_values;
use function assert;
use function count; use function count;
use function dirname; use function dirname;
use function explode;
use function file_put_contents; use function file_put_contents;
use function fwrite; use function fwrite;
use function implode;
use function json_encode; use function json_encode;
use function ksort; use function ksort;
use function usort; use function usort;
@ -56,9 +65,22 @@ class BlockStateMapping{
){} ){}
} }
/**
* @param Tag[] $properties
* @phpstan-param array<string, Tag> $properties
*/
function encodeOrderedProperties(array $properties) : string{
ksort($properties, SORT_STRING);
return implode("", array_map(fn(Tag $tag) => encodeProperty($tag), array_values($properties)));
}
function encodeProperty(Tag $tag) : string{
return (new LittleEndianNbtSerializer())->write(new TreeRoot($tag));
}
/** /**
* @return BlockStateMapping[][] * @return BlockStateMapping[][]
* @phpstan-return array<string, list<BlockStateMapping>> * @phpstan-return array<string, array<string, BlockStateMapping>>
*/ */
function loadUpgradeTable(string $file, bool $reverse) : array{ function loadUpgradeTable(string $file, bool $reverse) : array{
$contents = Filesystem::fileGetContents($file); $contents = Filesystem::fileGetContents($file);
@ -72,7 +94,7 @@ function loadUpgradeTable(string $file, bool $reverse) : array{
$old = BlockStateData::fromNbt($reverse ? $newTag : $oldTag); $old = BlockStateData::fromNbt($reverse ? $newTag : $oldTag);
$new = BlockStateData::fromNbt($reverse ? $oldTag : $newTag); $new = BlockStateData::fromNbt($reverse ? $oldTag : $newTag);
$result[$old->getName()][] = new BlockStateMapping( $result[$old->getName()][encodeOrderedProperties($old->getStates())] = new BlockStateMapping(
$old, $old,
$new $new
); );
@ -82,111 +104,176 @@ function loadUpgradeTable(string $file, bool $reverse) : array{
} }
/** /**
* @param true[] $removedPropertiesCache * @param BlockStateData[] $states
* @param Tag[][] $remappedPropertyValuesCache * @phpstan-param array<string, BlockStateData> $states
* @phpstan-param array<string, true> $removedPropertiesCache *
* @phpstan-param array<string, array<string, Tag>> $remappedPropertyValuesCache * @return Tag[][]
* @phpstan-return array<string, array<string, Tag>>
*/ */
function processState(BlockStateData $old, BlockStateData $new, BlockStateUpgradeSchema $result, array &$removedPropertiesCache, array &$remappedPropertyValuesCache) : void{ function buildStateGroupSchema(array $states) : ?array{
$first = $states[array_key_first($states)];
//new and old IDs are the same; compare states $properties = [];
$oldName = $old->getName(); foreach(Utils::stringifyKeys($first->getStates()) as $propertyName => $propertyValue){
$properties[$propertyName][encodeProperty($propertyValue)] = $propertyValue;
}
foreach($states as $state){
if(count($state->getStates()) !== count($properties)){
return null;
}
foreach(Utils::stringifyKeys($state->getStates()) as $propertyName => $propertyValue){
if(!isset($properties[$propertyName])){
return null;
}
$properties[$propertyName][encodeProperty($propertyValue)] = $propertyValue;
}
}
$oldStates = $old->getStates(); return $properties;
$newStates = $new->getStates(); }
$propertyRemoved = []; /**
$propertyAdded = []; * @param BlockStateMapping[] $upgradeTable
foreach(Utils::stringifyKeys($oldStates) as $propertyName => $oldProperty){ * @phpstan-param array<string, BlockStateMapping> $upgradeTable
$newProperty = $new->getState($propertyName); */
if($newProperty === null){ function processStateGroup(string $oldName, array $upgradeTable, BlockStateUpgradeSchema $result) : bool{
$propertyRemoved[$propertyName] = $oldProperty; $newProperties = buildStateGroupSchema(array_map(fn(BlockStateMapping $m) => $m->new, $upgradeTable));
}elseif(!$newProperty->equals($oldProperty)){ if($newProperties === null){
if(!isset($remappedPropertyValuesCache[$propertyName][$oldProperty->getValue()])){ \GlobalLogger::get()->warning("New states for $oldName don't all have the same set of properties - processing as remaps instead");
$result->remappedPropertyValues[$oldName][$propertyName][] = new BlockStateUpgradeSchemaValueRemap( return false;
$oldProperty, }
$newProperty $oldProperties = buildStateGroupSchema(array_map(fn(BlockStateMapping $m) => $m->old, $upgradeTable));
); if($oldProperties === null){
$remappedPropertyValuesCache[$propertyName][$oldProperty->getValue()] = $newProperty; //TODO: not sure if this is actually required - we may be able to apply some transformations even if the states are not consistent
//however, this should never normally occur anyway
\GlobalLogger::get()->warning("Old states for $oldName don't all have the same set of properties - processing as remaps instead");
return false;
}
$remappedPropertyValues = [];
$addedProperties = [];
$removedProperties = [];
$renamedProperties = [];
foreach(Utils::stringifyKeys($newProperties) as $newPropertyName => $newPropertyValues){
if(count($newPropertyValues) === 1){
$newPropertyValue = $newPropertyValues[array_key_first($newPropertyValues)];
if(isset($oldProperties[$newPropertyName])){
//all the old values for this property were mapped to the same new value
//it would be more space-efficient to represent this as a remove+add, but we can't guarantee that the
//removal of the old value will be done before the addition of the new value
foreach($oldProperties[$newPropertyName] as $oldPropertyValue){
$remappedPropertyValues[$newPropertyName][encodeProperty($oldPropertyValue)] = $newPropertyValue;
}
}else{
//this property has no relation to any property value in any of the old states - it's a new property
$addedProperties[$newPropertyName] = $newPropertyValue;
} }
} }
} }
foreach(Utils::stringifyKeys($newStates) as $propertyName => $value){ foreach(Utils::stringifyKeys($oldProperties) as $oldPropertyName => $oldPropertyValues){
if($old->getState($propertyName) === null){ $mappingsContainingOldValue = [];
$propertyAdded[$propertyName] = $value; foreach($upgradeTable as $mapping){
} $mappingOldValue = $mapping->old->getState($oldPropertyName) ?? throw new AssumptionFailedError("This should never happen");
} foreach($oldPropertyValues as $oldPropertyValue){
if($mappingOldValue->equals($oldPropertyValue)){
if(count($propertyAdded) === 0 && count($propertyRemoved) === 0){ $mappingsContainingOldValue[encodeProperty($oldPropertyValue)][] = $mapping;
return; break;
}
if(count($propertyAdded) === 1 && count($propertyRemoved) === 1){
$propertyOldName = array_key_first($propertyRemoved);
$propertyNewName = array_key_first($propertyAdded);
$propertyOldValue = $propertyRemoved[$propertyOldName];
$propertyNewValue = $propertyAdded[$propertyNewName];
$existingPropertyValueMap = $remappedPropertyValuesCache[$propertyOldName][$propertyOldValue->getValue()] ?? null;
if($propertyOldName !== $propertyNewName){
if(!$propertyOldValue->equals($propertyNewValue) && $existingPropertyValueMap === null){
\GlobalLogger::get()->warning("warning: guessing that $oldName has $propertyOldName renamed to $propertyNewName with a value map of $propertyOldValue mapped to $propertyNewValue");;
}
//this is a guess; it might not be reliable if the value changed as well
//this will probably never be an issue, but it might rear its ugly head in the future
$result->renamedProperties[$oldName][$propertyOldName] = $propertyNewName;
}
if(!$propertyOldValue->equals($propertyNewValue)){
$mapped = true;
if($existingPropertyValueMap !== null && !$existingPropertyValueMap->equals($propertyNewValue)){
if($existingPropertyValueMap->equals($propertyOldValue)){
\GlobalLogger::get()->warning("warning: guessing that the value $propertyOldValue of $propertyNewValue did not change");;
$mapped = false;
}else{
\GlobalLogger::get()->warning("warning: mismatch of new value for $propertyNewName for $oldName: $propertyOldValue seen mapped to $propertyNewValue and $existingPropertyValueMap");;
} }
} }
if($mapped && !isset($remappedPropertyValuesCache[$propertyOldName][$propertyOldValue->getValue()])){
//value remap
$result->remappedPropertyValues[$oldName][$propertyOldName][] = new BlockStateUpgradeSchemaValueRemap(
$propertyRemoved[$propertyOldName],
$propertyAdded[$propertyNewName]
);
$remappedPropertyValuesCache[$propertyOldName][$propertyOldValue->getValue()] = $propertyNewValue;
}
}elseif($existingPropertyValueMap !== null){
\GlobalLogger::get()->warning("warning: multiple values found for value $propertyOldValue of $propertyNewName on block $oldName, guessing it did not change");;
$remappedPropertyValuesCache[$propertyOldName][$propertyOldValue->getValue()] = $propertyNewValue;
} }
}else{
if(count($propertyAdded) !== 0 && count($propertyRemoved) === 0){
foreach(Utils::stringifyKeys($propertyAdded) as $propertyAddedName => $propertyAddedValue){
$existingDefault = $result->addedProperties[$oldName][$propertyAddedName] ?? null;
if($existingDefault !== null && !$existingDefault->equals($propertyAddedValue)){
throw new \UnexpectedValueException("Ambiguous default value for added property $propertyAddedName on block $oldName");
}
$result->addedProperties[$oldName][$propertyAddedName] = $propertyAddedValue; $candidateNewPropertyNames = [];
//foreach mappings by unique value, compute the diff across all the states in the list
foreach(Utils::stringifyKeys($mappingsContainingOldValue) as $rawOldValue => $mappingList){
$first = array_shift($mappingList);
foreach(Utils::stringifyKeys($first->new->getStates()) as $newPropertyName => $newPropertyValue){
if(isset($addedProperties[$newPropertyName])){
//this property was already determined to be unrelated to any old property
continue;
}
foreach($mappingList as $pair){
if(!($pair->new->getState($newPropertyName)?->equals($newPropertyValue) ?? false)){
//if the new property is different with an unchanged old value,
//the property may be influenced by multiple old properties, or be unrelated entirely
continue 2;
}
}
$candidateNewPropertyNames[$newPropertyName][$rawOldValue] = $newPropertyValue;
} }
}elseif(count($propertyRemoved) !== 0 && count($propertyAdded) === 0){ }
foreach(Utils::stringifyKeys($propertyRemoved) as $propertyRemovedName => $propertyRemovedValue){
if(!isset($removedPropertiesCache[$propertyRemovedName])){ if(count($candidateNewPropertyNames) === 0){
//to avoid having useless keys in the output $removedProperties[$oldPropertyName] = $oldPropertyName;
$result->removedProperties[$oldName][] = $propertyRemovedName; }elseif(count($candidateNewPropertyNames) === 1){
$removedPropertiesCache[$propertyRemovedName] = $propertyRemovedName; $newPropertyName = array_key_first($candidateNewPropertyNames);
$newPropertyValues = $candidateNewPropertyNames[$newPropertyName];
if($oldPropertyName !== $newPropertyName){
$renamedProperties[$oldPropertyName] = $newPropertyName;
}
foreach(Utils::stringifyKeys($newPropertyValues) as $rawOldValue => $newPropertyValue){
if(!$newPropertyValue->equals($oldPropertyValues[$rawOldValue])){
$remappedPropertyValues[$oldPropertyName][$rawOldValue] = $newPropertyValue;
} }
} }
}else{ }else{
$result->remappedStates[$oldName][] = new BlockStateUpgradeSchemaBlockRemap( $split = true;
$oldStates, if(isset($candidateNewPropertyNames[$oldPropertyName])){
$new->getName(), //In 1.10, direction wasn't changed at all, but not all state permutations were present in the palette,
$newStates, //making it appear that door_hinge_bit was correlated with direction.
[] //If a new property is present with the same name and values as an old property, we can assume that
); //the property was unchanged, and that any extra matches properties are probably unrelated.
\GlobalLogger::get()->warning("warning: multiple properties added and removed for $oldName; added full state remap");; $changedValues = false;
foreach(Utils::stringifyKeys($candidateNewPropertyNames[$oldPropertyName]) as $rawOldValue => $newPropertyValue){
if(!$newPropertyValue->equals($oldPropertyValues[$rawOldValue])){
//if any of the new values are different, we may be dealing with a property being split into
//multiple new properties - hand this off to the remap handler
$changedValues = true;
break;
}
}
if(!$changedValues){
$split = false;
}
}
if($split){
\GlobalLogger::get()->warning(
"Multiple new properties (" . (implode(", ", array_keys($candidateNewPropertyNames))) . ") are correlated with $oldName property $oldPropertyName, processing as remaps instead"
);
return false;
}else{
//is it safe to ignore the rest?
}
} }
} }
//finally, write the results to the schema
if(count($remappedPropertyValues) !== 0){
foreach(Utils::stringifyKeys($remappedPropertyValues) as $oldPropertyName => $propertyValues){
foreach(Utils::stringifyKeys($propertyValues) as $rawOldValue => $newPropertyValue){
$oldPropertyValue = $oldProperties[$oldPropertyName][$rawOldValue];
$result->remappedPropertyValues[$oldName][$oldPropertyName][] = new BlockStateUpgradeSchemaValueRemap(
$oldPropertyValue,
$newPropertyValue
);
}
}
}
if(count($addedProperties) !== 0){
$result->addedProperties[$oldName] = $addedProperties;
}
if(count($removedProperties) !== 0){
$result->removedProperties[$oldName] = array_values($removedProperties);
}
if(count($renamedProperties) !== 0){
$result->renamedProperties[$oldName] = $renamedProperties;
}
return true;
} }
/** /**
@ -194,17 +281,18 @@ function processState(BlockStateData $old, BlockStateData $new, BlockStateUpgrad
* This significantly reduces the output size during flattening when the flattened block has many permutations * This significantly reduces the output size during flattening when the flattened block has many permutations
* (e.g. walls). * (e.g. walls).
* *
* @param BlockStateUpgradeSchemaBlockRemap[] $stateRemaps * @param BlockStateMapping[] $upgradeTable
* @param BlockStateMapping[] $upgradeTable * @phpstan-param array<string, BlockStateMapping> $upgradeTable
* *
* @return BlockStateUpgradeSchemaBlockRemap[] * @return BlockStateUpgradeSchemaBlockRemap[]
* @phpstan-return list<BlockStateUpgradeSchemaBlockRemap>
*/ */
function compressRemappedStates(array $upgradeTable, array $stateRemaps) : array{ function processRemappedStates(array $upgradeTable) : array{
$unchangedStatesByNewName = []; $unchangedStatesByNewName = [];
foreach($upgradeTable as $pair){ foreach($upgradeTable as $pair){
if(count($pair->old->getStates()) === 0 || count($pair->new->getStates()) === 0){ if(count($pair->old->getStates()) === 0 || count($pair->new->getStates()) === 0){
//all states have changed in some way - compression not possible //all states have changed in some way - no states are copied over
$unchangedStatesByNewName[$pair->new->getName()] = []; $unchangedStatesByNewName[$pair->new->getName()] = [];
continue; continue;
} }
@ -240,78 +328,115 @@ function compressRemappedStates(array $upgradeTable, array $stateRemaps) : array
$unchangedStatesByNewName[$newName] = $unchangedStates; $unchangedStatesByNewName[$newName] = $unchangedStates;
} }
$compressedRemaps = []; $flattenedProperties = [];
$notFlattenedProperties = [];
$notFlattenedPropertyValues = [];
foreach($upgradeTable as $pair){
foreach(Utils::stringifyKeys($pair->old->getStates()) as $propertyName => $propertyValue){
if(isset($notFlattenedProperties[$propertyName])){
continue;
}
if(!$propertyValue instanceof StringTag){
$notFlattenedProperties[$propertyName] = true;
continue;
}
$rawValue = $propertyValue->getValue();
if($rawValue === ""){
$notFlattenedProperties[$propertyName] = true;
continue;
}
$parts = explode($rawValue, $pair->new->getName(), 2);
if(count($parts) !== 2){
//the new name does not contain the property value, but it may still be able to be flattened in other cases
$notFlattenedPropertyValues[$propertyName][$rawValue] = $rawValue;
continue;
}
[$prefix, $suffix] = $parts;
foreach($stateRemaps as $remap){ $filter = $pair->old->getStates();
$oldState = $remap->oldState; foreach($unchangedStatesByNewName[$pair->new->getName()] as $unchangedPropertyName){
$newState = $remap->newState; unset($filter[$unchangedPropertyName]);
}
if($oldState === null || $newState === null){ unset($filter[$propertyName]);
//no unchanged states - no compression possible $rawFilter = encodeOrderedProperties($filter);
assert(!isset($unchangedStatesByNewName[$remap->newName])); $flattenRule = new BlockStateUpgradeSchemaFlattenedName(
$compressedRemaps[$remap->newName][] = $remap; prefix: $prefix,
continue; flattenedProperty: $propertyName,
suffix: $suffix
);
if(!isset($flattenedProperties[$propertyName][$rawFilter])){
$flattenedProperties[$propertyName][$rawFilter] = $flattenRule;
}elseif(!$flattenRule->equals($flattenedProperties[$propertyName][$rawFilter])){
$notFlattenedProperties[$propertyName] = true;
}
} }
}
foreach(Utils::stringifyKeys($notFlattenedProperties) as $propertyName => $_){
unset($flattenedProperties[$propertyName]);
}
ksort($flattenedProperties, SORT_STRING);
$flattenProperty = array_key_first($flattenedProperties);
$list = [];
foreach($upgradeTable as $pair){
$oldState = $pair->old->getStates();
$newState = $pair->new->getStates();
$cleanedOldState = $oldState; $cleanedOldState = $oldState;
$cleanedNewState = $newState; $cleanedNewState = $newState;
$newName = $pair->new->getName();
foreach($unchangedStatesByNewName[$remap->newName] as $propertyName){ foreach($unchangedStatesByNewName[$newName] as $propertyName){
unset($cleanedOldState[$propertyName]); unset($cleanedOldState[$propertyName]);
unset($cleanedNewState[$propertyName]); unset($cleanedNewState[$propertyName]);
} }
ksort($cleanedOldState); ksort($cleanedOldState);
ksort($cleanedNewState); ksort($cleanedNewState);
$flattened = false;
$duplicate = false; if($flattenProperty !== null){
$compressedRemaps[$remap->newName] ??= []; $flattenedValue = $cleanedOldState[$flattenProperty] ?? null;
foreach($compressedRemaps[$remap->newName] as $k => $compressedRemap){ if(!$flattenedValue instanceof StringTag){
assert($compressedRemap->oldState !== null && $compressedRemap->newState !== null); throw new AssumptionFailedError("This should always be a TAG_String");
if(
count($compressedRemap->oldState) !== count($cleanedOldState) ||
count($compressedRemap->newState) !== count($cleanedNewState)
){
continue;
} }
foreach(Utils::stringifyKeys($cleanedOldState) as $propertyName => $propertyValue){ if(!isset($notFlattenedPropertyValues[$flattenProperty][$flattenedValue->getValue()])){
if(!isset($compressedRemap->oldState[$propertyName]) || !$compressedRemap->oldState[$propertyName]->equals($propertyValue)){ unset($cleanedOldState[$flattenProperty]);
//different filter value $flattened = true;
continue 2;
}
} }
foreach(Utils::stringifyKeys($cleanedNewState) as $propertyName => $propertyValue){
if(!isset($compressedRemap->newState[$propertyName]) || !$compressedRemap->newState[$propertyName]->equals($propertyValue)){
//different replacement value
continue 2;
}
}
$duplicate = true;
break;
} }
if(!$duplicate){ $rawOldState = encodeOrderedProperties($cleanedOldState);
$compressedRemaps[$remap->newName][] = new BlockStateUpgradeSchemaBlockRemap( $newNameRule = $flattenProperty !== null && $flattened ?
$cleanedOldState, $flattenedProperties[$flattenProperty][$rawOldState] ?? throw new AssumptionFailedError("This should always be set") :
$remap->newName, $newName;
$cleanedNewState,
$unchangedStatesByNewName[$remap->newName] $remap = new BlockStateUpgradeSchemaBlockRemap(
); $cleanedOldState,
$newNameRule,
$cleanedNewState,
$unchangedStatesByNewName[$pair->new->getName()]
);
$existing = $list[$rawOldState] ?? null;
if($existing === null || $existing->equals($remap)){
$list[$rawOldState] = $remap;
}else{
//match criteria is borked
throw new AssumptionFailedError("Match criteria resulted in two ambiguous remaps");
} }
} }
$list = array_merge(...array_values($compressedRemaps));
//more specific filters must come before less specific ones, in case of a remap on a certain value which is //more specific filters must come before less specific ones, in case of a remap on a certain value which is
//otherwise unchanged //otherwise unchanged
usort($list, function(BlockStateUpgradeSchemaBlockRemap $a, BlockStateUpgradeSchemaBlockRemap $b) : int{ usort($list, function(BlockStateUpgradeSchemaBlockRemap $a, BlockStateUpgradeSchemaBlockRemap $b) : int{
return count($b->oldState) <=> count($a->oldState); return count($b->oldState) <=> count($a->oldState);
}); });
return $list; return array_values($list);
} }
/** /**
* @param BlockStateMapping[][] $upgradeTable * @param BlockStateMapping[][] $upgradeTable
* @phpstan-param array<string, list<BlockStateMapping>> $upgradeTable * @phpstan-param array<string, array<string, BlockStateMapping>> $upgradeTable
*/ */
function generateBlockStateUpgradeSchema(array $upgradeTable) : BlockStateUpgradeSchema{ function generateBlockStateUpgradeSchema(array $upgradeTable) : BlockStateUpgradeSchema{
$foundVersion = -1; $foundVersion = -1;
@ -343,8 +468,6 @@ function generateBlockStateUpgradeSchema(array $upgradeTable) : BlockStateUpgrad
foreach(Utils::stringifyKeys($upgradeTable) as $oldName => $blockStateMappings){ foreach(Utils::stringifyKeys($upgradeTable) as $oldName => $blockStateMappings){
$newNameFound = []; $newNameFound = [];
$removedPropertiesCache = [];
$remappedPropertyValuesCache = [];
foreach($blockStateMappings as $mapping){ foreach($blockStateMappings as $mapping){
$newName = $mapping->new->getName(); $newName = $mapping->new->getName();
$newNameFound[$newName] = true; $newNameFound[$newName] = true;
@ -354,35 +477,23 @@ function generateBlockStateUpgradeSchema(array $upgradeTable) : BlockStateUpgrad
if($newName !== $oldName){ if($newName !== $oldName){
$result->renamedIds[$oldName] = array_key_first($newNameFound); $result->renamedIds[$oldName] = array_key_first($newNameFound);
} }
foreach($blockStateMappings as $mapping){ if(!processStateGroup($oldName, $blockStateMappings, $result)){
processState($mapping->old, $mapping->new, $result, $removedPropertiesCache, $remappedPropertyValuesCache); throw new \RuntimeException("States with the same ID should be fully consistent");
} }
}else{ }else{
if(isset($newNameFound[$oldName])){ if(isset($newNameFound[$oldName])){
//some of the states stayed under the same ID - we can process these as normal states //some of the states stayed under the same ID - we can process these as normal states
foreach($blockStateMappings as $k => $mapping){ $stateGroup = array_filter($blockStateMappings, fn(BlockStateMapping $m) => $m->new->getName() === $oldName);
if($mapping->new->getName() === $oldName){ if(processStateGroup($oldName, $stateGroup, $result)){
processState($mapping->old, $mapping->new, $result, $removedPropertiesCache, $remappedPropertyValuesCache); foreach(Utils::stringifyKeys($stateGroup) as $k => $mapping){
unset($blockStateMappings[$k]); unset($blockStateMappings[$k]);
} }
} }
} }
//block mapped to multiple different new IDs; we can't guess these, so we just do a plain old remap //block mapped to multiple different new IDs; we can't guess these, so we just do a plain old remap
foreach($blockStateMappings as $mapping){ $result->remappedStates[$oldName] = processRemappedStates($blockStateMappings);
if(!$mapping->old->equals($mapping->new)){
$result->remappedStates[$mapping->old->getName()][] = new BlockStateUpgradeSchemaBlockRemap(
$mapping->old->getStates(),
$mapping->new->getName(),
$mapping->new->getStates(),
[]
);
}
}
} }
} }
foreach(Utils::stringifyKeys($result->remappedStates) as $oldName => $remap){
$result->remappedStates[$oldName] = compressRemappedStates($upgradeTable[$oldName], $remap);
}
return $result; return $result;
} }