mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-22 00:33:59 +00:00
Separate block legacy data upgrading from block deserialization
this commit provides a central place where all block data can go to be upgraded to the latest version (currently 1.19), irrespective of how old it is. Previously I had issues during debugging, because it wasn't possible to just upgrade a block without deserializing it into a Block object, which isn't currently supported for many blocks. This commit solves that problem by separating the upgrading from the deserialization.
This commit is contained in:
parent
301b0aba82
commit
1533fcf8f6
@ -46,11 +46,13 @@ class FlowerPot extends Spawnable{
|
||||
|
||||
public function readSaveData(CompoundTag $nbt) : void{
|
||||
$blockStateData = null;
|
||||
|
||||
$blockDataUpgrader = GlobalBlockStateHandlers::getUpgrader();
|
||||
if(($itemIdTag = $nbt->getTag(self::TAG_ITEM)) instanceof ShortTag && ($itemMetaTag = $nbt->getTag(self::TAG_ITEM_DATA)) instanceof IntTag){
|
||||
$blockStateData = GlobalBlockStateHandlers::getLegacyBlockStateMapper()->fromIntIdMeta($itemIdTag->getValue(), $itemMetaTag->getValue());
|
||||
$blockStateData = $blockDataUpgrader->upgradeIntIdMeta($itemIdTag->getValue(), $itemMetaTag->getValue());
|
||||
}elseif(($plantBlockTag = $nbt->getCompoundTag(self::TAG_PLANT_BLOCK)) !== null){
|
||||
try{
|
||||
$blockStateData = GlobalBlockStateHandlers::nbtToBlockStateData($plantBlockTag);
|
||||
$blockStateData = $blockDataUpgrader->upgradeBlockStateNbt($plantBlockTag);
|
||||
}catch(BlockStateDeserializeException $e){
|
||||
throw new SavedDataLoadingException("Error loading " . self::TAG_PLANT_BLOCK . " tag for flower pot: " . $e->getMessage(), 0, $e);
|
||||
}
|
||||
|
@ -1,40 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\data\bedrock\block;
|
||||
|
||||
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgrader;
|
||||
|
||||
final class UpgradingBlockStateDeserializer implements BlockStateDeserializer{
|
||||
|
||||
public function __construct(
|
||||
private BlockStateUpgrader $blockStateUpgrader,
|
||||
private BlockStateDeserializer $realDeserializer
|
||||
){}
|
||||
|
||||
public function deserialize(BlockStateData $stateData) : int{
|
||||
return $this->realDeserializer->deserialize($this->blockStateUpgrader->upgrade($stateData));
|
||||
}
|
||||
|
||||
public function getRealDeserializer() : BlockStateDeserializer{ return $this->realDeserializer; }
|
||||
}
|
@ -65,12 +65,9 @@ class FallingBlock extends Entity{
|
||||
public static function parseBlockNBT(BlockFactory $factory, CompoundTag $nbt) : Block{
|
||||
|
||||
//TODO: 1.8+ save format
|
||||
$blockDataUpgrader = GlobalBlockStateHandlers::getUpgrader();
|
||||
if(($fallingBlockTag = $nbt->getCompoundTag(self::TAG_FALLING_BLOCK)) !== null){
|
||||
try{
|
||||
$blockStateData = GlobalBlockStateHandlers::nbtToBlockStateData($fallingBlockTag);
|
||||
}catch(BlockStateDeserializeException $e){
|
||||
throw new SavedDataLoadingException($e->getMessage(), 0, $e);
|
||||
}
|
||||
$blockStateData = $blockDataUpgrader->upgradeBlockStateNbt($fallingBlockTag);
|
||||
}else{
|
||||
if(($tileIdTag = $nbt->getTag("TileID")) instanceof IntTag){
|
||||
$blockId = $tileIdTag->getValue();
|
||||
@ -81,10 +78,10 @@ class FallingBlock extends Entity{
|
||||
}
|
||||
$damage = $nbt->getByte("Data", 0);
|
||||
|
||||
$blockStateData = GlobalBlockStateHandlers::getLegacyBlockStateMapper()->fromIntIdMeta($blockId, $damage);
|
||||
if($blockStateData === null){
|
||||
throw new SavedDataLoadingException("Invalid legacy falling block");
|
||||
}
|
||||
$blockStateData = $blockDataUpgrader->upgradeIntIdMeta($blockId, $damage);
|
||||
}
|
||||
if($blockStateData === null){
|
||||
throw new SavedDataLoadingException("Invalid legacy falling block");
|
||||
}
|
||||
|
||||
try{
|
||||
|
@ -56,11 +56,11 @@ abstract class BaseWorldProvider implements WorldProvider{
|
||||
|
||||
//TODO: this should be dependency-injected so it can be replaced, but that would break BC
|
||||
//also, we want it to be lazy-loaded ...
|
||||
$legacyBlockStateMapper = GlobalBlockStateHandlers::getLegacyBlockStateMapper();
|
||||
$blockDataUpgrader = GlobalBlockStateHandlers::getUpgrader();
|
||||
$blockStateDeserializer = GlobalBlockStateHandlers::getDeserializer();
|
||||
$newPalette = [];
|
||||
foreach($palette as $k => $legacyIdMeta){
|
||||
$newStateData = $legacyBlockStateMapper->fromIntIdMeta($legacyIdMeta >> 4, $legacyIdMeta & 0xf);
|
||||
$newStateData = $blockDataUpgrader->upgradeIntIdMeta($legacyIdMeta >> 4, $legacyIdMeta & 0xf);
|
||||
if($newStateData === null){
|
||||
//TODO: remember data for unknown states so we can implement them later
|
||||
$newStateData = new BlockStateData(BlockTypeNames::INFO_UPDATE, CompoundTag::create(), BlockStateData::CURRENT_VERSION);
|
||||
|
@ -26,18 +26,16 @@ namespace pocketmine\world\format\io;
|
||||
use pocketmine\data\bedrock\block\BlockStateData;
|
||||
use pocketmine\data\bedrock\block\BlockStateDeserializer;
|
||||
use pocketmine\data\bedrock\block\BlockStateSerializer;
|
||||
use pocketmine\data\bedrock\block\BlockTypeNames;
|
||||
use pocketmine\data\bedrock\block\CachingBlockStateDeserializer;
|
||||
use pocketmine\data\bedrock\block\CachingBlockStateSerializer;
|
||||
use pocketmine\data\bedrock\block\convert\BlockObjectToBlockStateSerializer;
|
||||
use pocketmine\data\bedrock\block\convert\BlockStateToBlockObjectDeserializer;
|
||||
use pocketmine\data\bedrock\block\upgrade\BlockDataUpgrader;
|
||||
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgrader;
|
||||
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaUtils;
|
||||
use pocketmine\data\bedrock\block\upgrade\LegacyBlockStateMapper;
|
||||
use pocketmine\data\bedrock\block\UpgradingBlockStateDeserializer;
|
||||
use pocketmine\data\bedrock\block\upgrade\LegacyBlockIdToStringIdMap;
|
||||
use pocketmine\data\bedrock\block\upgrade\LegacyBlockStateMapper;
|
||||
use pocketmine\errorhandler\ErrorToExceptionHandler;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use Webmozart\PathUtil\Path;
|
||||
use function file_get_contents;
|
||||
use const pocketmine\BEDROCK_BLOCK_UPGRADE_SCHEMA_PATH;
|
||||
@ -54,50 +52,29 @@ final class GlobalBlockStateHandlers{
|
||||
|
||||
private static ?BlockStateDeserializer $blockStateDeserializer;
|
||||
|
||||
private static ?LegacyBlockStateMapper $legacyBlockStateMapper;
|
||||
private static ?BlockDataUpgrader $blockDataUpgrader;
|
||||
|
||||
public static function getDeserializer() : BlockStateDeserializer{
|
||||
return self::$blockStateDeserializer ??= new CachingBlockStateDeserializer(
|
||||
new UpgradingBlockStateDeserializer(
|
||||
new BlockStateUpgrader(BlockStateUpgradeSchemaUtils::loadSchemas(
|
||||
Path::join(BEDROCK_BLOCK_UPGRADE_SCHEMA_PATH, 'nbt_upgrade_schema'),
|
||||
BlockStateData::CURRENT_VERSION
|
||||
)),
|
||||
new BlockStateToBlockObjectDeserializer()
|
||||
)
|
||||
);
|
||||
return self::$blockStateDeserializer ??= new CachingBlockStateDeserializer(new BlockStateToBlockObjectDeserializer());
|
||||
}
|
||||
|
||||
public static function getSerializer() : BlockStateSerializer{
|
||||
return self::$blockStateSerializer ??= new CachingBlockStateSerializer(new BlockObjectToBlockStateSerializer());
|
||||
}
|
||||
|
||||
public static function getLegacyBlockStateMapper() : LegacyBlockStateMapper{
|
||||
return self::$legacyBlockStateMapper ??= LegacyBlockStateMapper::loadFromString(
|
||||
ErrorToExceptionHandler::trapAndRemoveFalse(fn() => file_get_contents(Path::join(
|
||||
BEDROCK_BLOCK_UPGRADE_SCHEMA_PATH,
|
||||
'1.12.0_to_1.18.10_blockstate_map.bin'
|
||||
))),
|
||||
LegacyBlockIdToStringIdMap::getInstance()
|
||||
public static function getUpgrader() : BlockDataUpgrader{
|
||||
return self::$blockDataUpgrader ??= new BlockDataUpgrader(
|
||||
LegacyBlockStateMapper::loadFromString(
|
||||
ErrorToExceptionHandler::trapAndRemoveFalse(fn() => file_get_contents(Path::join(
|
||||
BEDROCK_BLOCK_UPGRADE_SCHEMA_PATH,
|
||||
'1.12.0_to_1.18.10_blockstate_map.bin'
|
||||
))),
|
||||
LegacyBlockIdToStringIdMap::getInstance()
|
||||
),
|
||||
new BlockStateUpgrader(BlockStateUpgradeSchemaUtils::loadSchemas(
|
||||
Path::join(BEDROCK_BLOCK_UPGRADE_SCHEMA_PATH, 'nbt_upgrade_schema'),
|
||||
BlockStateData::CURRENT_VERSION
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
public static function nbtToBlockStateData(CompoundTag $tag) : BlockStateData{
|
||||
if($tag->getTag("name") !== null && $tag->getTag("val") !== null){
|
||||
//Legacy (pre-1.13) blockstate - upgrade it to a version we understand
|
||||
$id = $tag->getString("name");
|
||||
$data = $tag->getShort("val");
|
||||
|
||||
$blockStateData = GlobalBlockStateHandlers::getLegacyBlockStateMapper()->fromStringIdMeta($id, $data);
|
||||
if($blockStateData === null){
|
||||
//unknown block, invalid ID
|
||||
$blockStateData = new BlockStateData(BlockTypeNames::INFO_UPDATE, CompoundTag::create(), BlockStateData::CURRENT_VERSION);
|
||||
}
|
||||
}else{
|
||||
//Modern (post-1.13) blockstate
|
||||
$blockStateData = BlockStateData::fromNbt($tag);
|
||||
}
|
||||
|
||||
return $blockStateData;
|
||||
}
|
||||
}
|
||||
|
@ -160,12 +160,18 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
|
||||
|
||||
$paletteSize = $bitsPerBlock === 0 ? 1 : $stream->getLInt();
|
||||
|
||||
$blockDataUpgrader = GlobalBlockStateHandlers::getUpgrader();
|
||||
$blockStateDeserializer = GlobalBlockStateHandlers::getDeserializer();
|
||||
for($i = 0; $i < $paletteSize; ++$i){
|
||||
try{
|
||||
$offset = $stream->getOffset();
|
||||
|
||||
$blockStateData = GlobalBlockStateHandlers::nbtToBlockStateData($nbt->read($stream->getBuffer(), $offset)->mustGetCompoundTag());
|
||||
$blockStateNbt = $nbt->read($stream->getBuffer(), $offset)->mustGetCompoundTag();
|
||||
$blockStateData = $blockDataUpgrader->upgradeBlockStateNbt($blockStateNbt);
|
||||
if($blockStateData === null){
|
||||
//upgrading blockstates should always succeed, regardless of whether they've been implemented or not
|
||||
throw new BlockStateDeserializeException("Invalid or improperly mapped legacy blockstate: " . $blockStateNbt->toString());
|
||||
}
|
||||
$stream->setOffset($offset);
|
||||
|
||||
try{
|
||||
@ -210,7 +216,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
|
||||
$binaryStream = new BinaryStream($extraRawData);
|
||||
$count = $binaryStream->getLInt();
|
||||
|
||||
$legacyMapper = GlobalBlockStateHandlers::getLegacyBlockStateMapper();
|
||||
$blockDataUpgrader = GlobalBlockStateHandlers::getUpgrader();
|
||||
$blockStateDeserializer = GlobalBlockStateHandlers::getDeserializer();
|
||||
for($i = 0; $i < $count; ++$i){
|
||||
$key = $binaryStream->getLInt();
|
||||
@ -223,7 +229,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
|
||||
|
||||
$blockId = $value & 0xff;
|
||||
$blockData = ($value >> 8) & 0xf;
|
||||
$blockStateData = $legacyMapper->fromIntIdMeta($blockId, $blockData);
|
||||
$blockStateData = $blockDataUpgrader->upgradeIntIdMeta($blockId, $blockData);
|
||||
if($blockStateData === null){
|
||||
//TODO: we could preserve this in case it's supported in the future, but this was historically only
|
||||
//used for grass anyway, so we probably don't need to care
|
||||
|
Loading…
x
Reference in New Issue
Block a user