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.

Revisions

  1. dktapps created this gist Aug 18, 2025.
    186 changes: 186 additions & 0 deletions mapMatrixFlattened.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,186 @@
    <?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))
    ]
    );