mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-10-20 07:39:42 +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:
@@ -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
|
||||
|
Reference in New Issue
Block a user