<?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\convert;
use pocketmine\block\Block;
use pocketmine\block\RuntimeBlockStateRegistry;
use pocketmine\block\Slab;
use pocketmine\block\Stair;
use pocketmine\block\Wood;
use pocketmine\data\bedrock\block\BlockStateData;
use pocketmine\data\bedrock\block\BlockStateDeserializeException;
use pocketmine\data\bedrock\block\BlockStateDeserializer;
use pocketmine\data\bedrock\block\convert\BlockStateDeserializerHelper as Helper;
use pocketmine\data\bedrock\block\convert\BlockStateReader as Reader;
use function array_key_exists;
use function count;
final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
/**
* @var \Closure[]
* @phpstan-var array<string, \Closure(Reader $in) : Block>
*/
private array $deserializeFuncs = [];
/**
* @var int[]
* @phpstan-var array<string, int>
*/
private array $simpleCache = [];
public function deserialize(BlockStateData $stateData) : int{
if(count($stateData->getStates()) === 0){
//if a block has zero properties, we can keep a map of string ID -> internal blockstate ID
return $this->simpleCache[$stateData->getName()] ??= $this->deserializeToStateId($stateData);
}
//we can't cache blocks that have properties - go ahead and deserialize the slow way
return $this->deserializeToStateId($stateData);
}
private function deserializeToStateId(BlockStateData $stateData) : int{
$stateId = $this->deserializeBlock($stateData)->getStateId();
//plugin devs seem to keep missing this and causing core crashes, so we need to verify this at the earliest
//available opportunity
if(!RuntimeBlockStateRegistry::getInstance()->hasStateId($stateId)){
throw new \LogicException("State ID $stateId returned by deserializer for " . $stateData->getName() . " is not registered in RuntimeBlockStateRegistry");
}
return $stateId;
}
/** @phpstan-param \Closure(Reader) : Block $c */
public function map(string $id, \Closure $c) : void{
$this->deserializeFuncs[$id] = $c;
$this->simpleCache = [];
}
/**
* Returns the existing data deserializer for the given ID, or null if none exists.
* This may be useful if you need to override a deserializer, but still want to be able to fall back to the original.
*
* @phpstan-return ?\Closure(Reader) : Block
*/
public function getDeserializerForId(string $id) : ?\Closure{
return $this->deserializeFuncs[$id] ?? null;
}
/**
* @deprecated
* @phpstan-param \Closure() : Block $getBlock
*/
public function mapSimple(string $id, \Closure $getBlock) : void{
$this->map($id, $getBlock);
}
/**
* @deprecated
* @phpstan-param \Closure(Reader) : Slab $getBlock
*/
public function mapSlab(string $singleId, string $doubleId, \Closure $getBlock) : void{
$this->map($singleId, fn(Reader $in) => Helper::decodeSingleSlab($getBlock($in), $in));
$this->map($doubleId, fn(Reader $in) => Helper::decodeDoubleSlab($getBlock($in), $in));
}
/**
* @deprecated
* @phpstan-param \Closure() : Stair $getBlock
*/
public function mapStairs(string $id, \Closure $getBlock) : void{
$this->map($id, fn(Reader $in) : Stair => Helper::decodeStairs($getBlock(), $in));
}
/**
* @deprecated
* @phpstan-param \Closure() : Wood $getBlock
*/
public function mapLog(string $unstrippedId, string $strippedId, \Closure $getBlock) : void{
$this->map($unstrippedId, fn(Reader $in) => Helper::decodeLog($getBlock(), false, $in));
$this->map($strippedId, fn(Reader $in) => Helper::decodeLog($getBlock(), true, $in));
}
/** @throws BlockStateDeserializeException */
public function deserializeBlock(BlockStateData $blockStateData) : Block{
$id = $blockStateData->getName();
if(!array_key_exists($id, $this->deserializeFuncs)){
throw new UnsupportedBlockStateException("Unknown block ID \"$id\"");
}
$reader = new Reader($blockStateData);
$block = $this->deserializeFuncs[$id]($reader);
$reader->checkUnreadProperties();
return $block;
}
}