Skip to content

Instantly share code, notes, and snippets.

@dktapps
Created August 18, 2025 19:35
Show Gist options
  • Save dktapps/3ccf09680113ffb573e409c802827fcd to your computer and use it in GitHub Desktop.
Save dktapps/3ccf09680113ffb573e409c802827fcd to your computer and use it in GitHub Desktop.
Proof of concept for a cursed hack for Minecraft flattened blocks in PocketMine-MP, based on `unify-block-serializers` branch
<?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);
use pocketmine\block\Block;
use pocketmine\block\CakeWithDyedCandle;
use pocketmine\block\Coral;
use pocketmine\block\utils\CoralType;
use pocketmine\block\utils\DyeColor;
use pocketmine\block\VanillaBlocks as Blocks;
use pocketmine\block\Wool;
use pocketmine\data\bedrock\block\BlockStateNames;
use pocketmine\data\bedrock\block\convert\BlockStateReader;
use pocketmine\data\bedrock\block\convert\BlockStateWriter;
use pocketmine\data\bedrock\block\convert\property\BoolProperty;
use pocketmine\data\bedrock\block\convert\property\EnumProperty;
use pocketmine\data\bedrock\block\convert\property\Property;
use pocketmine\data\bedrock\block\convert\ValueMappings;
require __DIR__ .'/vendor/autoload.php';
/**
* @param string[]|EnumProperty[] $components
* @phpstan-param list<string|EnumProperty<*, *>> $components
*
* @return string[][]
*/
function compilePermutations(array $components) : array{
$result = [];
foreach($components as $component){
$column = is_string($component) ? [$component] : array_keys($component->getEnumMap()->getValueToEnum());
if(count($result) === 0){
$result[] = $column;
}else{
$stepResult = [];
foreach($result as $parts){
foreach($column as $value){
$stepPart = $parts;
$stepPart[] = $value;
$stepResult[] = $stepPart;
}
}
$result = $stepResult;
}
}
return $result;
}
/**
* @param string[]|EnumProperty[] $idComponents
* @param Property[] $properties
*
* @phpstan-template TBlock of Block
* @phpstan-param TBlock $block
* @phpstan-param list<string|EnumProperty<TBlock, *>> $idComponents
* @phpstan-param list<Property<TBlock>> $properties
*/
function mapMatrixFlattened(
Block $block,
array $idComponents,
array $properties = []
) : void{
//This is a really cursed hack that lets us essentially write flattened properties as blockstate properties, and
//then pull them out to compile an ID :D
//This works surprisingly well and is much more elegant than I would've expected
$idProperties = array_filter($idComponents, fn($c) => !is_string($c));
$serializer = function(Block $block) use ($idComponents, $idProperties, $properties) : BlockStateWriter{
//serialize properties into the ID
$idWriter = new BlockStateWriter("dummy");
foreach($idProperties as $idProperty){
$idProperty->serialize($block, $idWriter);
}
$flattenedReader = new BlockStateReader($idWriter->getBlockStateData());
$id = "";
foreach($idComponents as $infix){
$id .= is_string($infix) ? $infix : $flattenedReader->readString($infix->getName());
}
//serialize actual properties
$realWriter = new BlockStateWriter($id);
foreach($properties as $property){
$property->serialize($block, $realWriter);
}
return $realWriter;
};
$deserializers = [];
foreach(compilePermutations($idComponents) as $idParts){
//deconstruct the ID into a fake state
//we can do this at registration time since there will be multiple deserializers
$id = implode("", $idParts);
$flattenedWriter = new BlockStateWriter("dummy");
foreach($idComponents as $k => $component){
if($component instanceof EnumProperty){
$fakeValue = $idParts[$k];
$flattenedWriter->writeString($component->getName(), $fakeValue);
}
}
$idReader = new BlockStateReader($flattenedWriter->getBlockStateData());
echo $id . ": " . $flattenedWriter->getBlockStateData()->toVanillaNbt() . "\n";
$deserializers[$id] = function(BlockStateReader $reader) use ($block, $idReader, $idProperties, $properties) : Block{
//deserialize properties from the ID
$block = clone $block;
foreach($idProperties as $component){
$component->deserialize($block, $idReader);
}
//deserialize actual properties
foreach($properties as $property){
$property->deserialize($block, $reader);
}
return $block;
};
}
}
enum Bool_{
case FALSE;
case TRUE;
}
ValueMappings::getInstance()->addEnum(Bool_::class, fn(Bool_ $v) => match($v){
Bool_::FALSE => "",
Bool_::TRUE => "dead_"
});
ValueMappings::getInstance()->addEnum(CoralType::class, fn(CoralType $v) => match($v){
CoralType::TUBE => "tube",
CoralType::BRAIN => "brain",
CoralType::BUBBLE => "bubble",
CoralType::FIRE => "fire",
CoralType::HORN => "horn"
});
mapMatrixFlattened(
block: Blocks::CORAL(),
idComponents: [
"minecraft:",
new EnumProperty("dead", Bool_::class, fn(Coral $b) => $b->isDead() ? Bool_::TRUE : Bool_::FALSE, fn(Coral $b, Bool_ $v) => $b->setDead($v === Bool_::TRUE)),
new EnumProperty("coral_type", CoralType::class, fn(Coral $b) => $b->getCoralType(), fn(Coral $b, CoralType $v) => $b->setCoralType($v)),
"_coral"
]
);
mapMatrixFlattened(
block: Blocks::WOOL(),
idComponents: [
"minecraft:",
new EnumProperty("color", DyeColor::class, fn(Wool $b) => $b->getColor(), fn(Wool $b, DyeColor $v) => $b->setColor($v)),
"_wool"
]
);
mapMatrixFlattened(
block: Blocks::CAKE_WITH_DYED_CANDLE(),
idComponents: [
"minecraft:",
new EnumProperty("color", DyeColor::class, fn(CakeWithDyedCandle $b) => $b->getColor(), fn(CakeWithDyedCandle $b, DyeColor $v) => $b->setColor($v)),
"_candle_cake"
],
properties: [
new BoolProperty(BlockStateNames::LIT, fn(CakeWithDyedCandle $b) => $b->isLit(), fn(CakeWithDyedCandle $b, bool $v) => $b->setLit($v))
]
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment