Reduce global usage in world providers

This commit is contained in:
Dylan K. Taylor 2023-04-13 12:05:37 +01:00
parent 499b29b53a
commit c3a2199f0e
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
2 changed files with 23 additions and 23 deletions

View File

@ -24,6 +24,9 @@ declare(strict_types=1);
namespace pocketmine\world\format\io; namespace pocketmine\world\format\io;
use pocketmine\data\bedrock\block\BlockStateDeserializeException; use pocketmine\data\bedrock\block\BlockStateDeserializeException;
use pocketmine\data\bedrock\block\BlockStateDeserializer;
use pocketmine\data\bedrock\block\BlockStateSerializer;
use pocketmine\data\bedrock\block\upgrade\BlockDataUpgrader;
use pocketmine\world\format\io\exception\CorruptedWorldException; use pocketmine\world\format\io\exception\CorruptedWorldException;
use pocketmine\world\format\io\exception\UnsupportedWorldFormatException; use pocketmine\world\format\io\exception\UnsupportedWorldFormatException;
use pocketmine\world\format\PalettedBlockArray; use pocketmine\world\format\PalettedBlockArray;
@ -33,6 +36,10 @@ use function file_exists;
abstract class BaseWorldProvider implements WorldProvider{ abstract class BaseWorldProvider implements WorldProvider{
protected WorldData $worldData; protected WorldData $worldData;
protected BlockStateDeserializer $blockStateDeserializer;
protected BlockDataUpgrader $blockDataUpgrader;
protected BlockStateSerializer $blockStateSerializer;
public function __construct( public function __construct(
protected string $path protected string $path
){ ){
@ -40,6 +47,11 @@ abstract class BaseWorldProvider implements WorldProvider{
throw new WorldException("World does not exist"); throw new WorldException("World does not exist");
} }
//TODO: this should not rely on singletons
$this->blockStateDeserializer = GlobalBlockStateHandlers::getDeserializer();
$this->blockDataUpgrader = GlobalBlockStateHandlers::getUpgrader();
$this->blockStateSerializer = GlobalBlockStateHandlers::getSerializer();
$this->worldData = $this->loadLevelData(); $this->worldData = $this->loadLevelData();
} }
@ -52,24 +64,20 @@ abstract class BaseWorldProvider implements WorldProvider{
private function translatePalette(PalettedBlockArray $blockArray) : PalettedBlockArray{ private function translatePalette(PalettedBlockArray $blockArray) : PalettedBlockArray{
$palette = $blockArray->getPalette(); $palette = $blockArray->getPalette();
//TODO: this should be dependency-injected so it can be replaced, but that would break BC
//also, we want it to be lazy-loaded ...
$blockDataUpgrader = GlobalBlockStateHandlers::getUpgrader();
$blockStateDeserializer = GlobalBlockStateHandlers::getDeserializer();
$newPalette = []; $newPalette = [];
foreach($palette as $k => $legacyIdMeta){ foreach($palette as $k => $legacyIdMeta){
$newStateData = $blockDataUpgrader->upgradeIntIdMeta($legacyIdMeta >> 4, $legacyIdMeta & 0xf); $newStateData = $this->blockDataUpgrader->upgradeIntIdMeta($legacyIdMeta >> 4, $legacyIdMeta & 0xf);
if($newStateData === null){ if($newStateData === null){
//TODO: remember data for unknown states so we can implement them later //TODO: remember data for unknown states so we can implement them later
$newStateData = GlobalBlockStateHandlers::getUnknownBlockStateData(); $newStateData = GlobalBlockStateHandlers::getUnknownBlockStateData();
} }
try{ try{
$newPalette[$k] = $blockStateDeserializer->deserialize($newStateData); $newPalette[$k] = $this->blockStateDeserializer->deserialize($newStateData);
}catch(BlockStateDeserializeException){ }catch(BlockStateDeserializeException){
//TODO: this needs to be logged //TODO: this needs to be logged
//TODO: maybe we can remember unknown states for later saving instead of discarding them and destroying maps... //TODO: maybe we can remember unknown states for later saving instead of discarding them and destroying maps...
$newPalette[$k] = $blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData()); $newPalette[$k] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData());
} }
} }

View File

@ -27,7 +27,6 @@ use pocketmine\block\Block;
use pocketmine\block\BlockTypeIds; use pocketmine\block\BlockTypeIds;
use pocketmine\data\bedrock\BiomeIds; use pocketmine\data\bedrock\BiomeIds;
use pocketmine\data\bedrock\block\BlockStateDeserializeException; use pocketmine\data\bedrock\block\BlockStateDeserializeException;
use pocketmine\data\bedrock\block\BlockStateSerializer;
use pocketmine\nbt\LittleEndianNbtSerializer; use pocketmine\nbt\LittleEndianNbtSerializer;
use pocketmine\nbt\NbtDataException; use pocketmine\nbt\NbtDataException;
use pocketmine\nbt\NbtException; use pocketmine\nbt\NbtException;
@ -158,14 +157,12 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
$paletteSize = $bitsPerBlock === 0 ? 1 : $stream->getLInt(); $paletteSize = $bitsPerBlock === 0 ? 1 : $stream->getLInt();
$blockDataUpgrader = GlobalBlockStateHandlers::getUpgrader();
$blockStateDeserializer = GlobalBlockStateHandlers::getDeserializer();
for($i = 0; $i < $paletteSize; ++$i){ for($i = 0; $i < $paletteSize; ++$i){
try{ try{
$offset = $stream->getOffset(); $offset = $stream->getOffset();
$blockStateNbt = $nbt->read($stream->getBuffer(), $offset)->mustGetCompoundTag(); $blockStateNbt = $nbt->read($stream->getBuffer(), $offset)->mustGetCompoundTag();
$blockStateData = $blockDataUpgrader->upgradeBlockStateNbt($blockStateNbt); $blockStateData = $this->blockDataUpgrader->upgradeBlockStateNbt($blockStateNbt);
if($blockStateData === null){ if($blockStateData === null){
//upgrading blockstates should always succeed, regardless of whether they've been implemented or not //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()); throw new BlockStateDeserializeException("Invalid or improperly mapped legacy blockstate: " . $blockStateNbt->toString());
@ -173,11 +170,11 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
$stream->setOffset($offset); $stream->setOffset($offset);
try{ try{
$palette[] = $blockStateDeserializer->deserialize($blockStateData); $palette[] = $this->blockStateDeserializer->deserialize($blockStateData);
}catch(BlockStateDeserializeException){ }catch(BlockStateDeserializeException){
//TODO: remember data for unknown states so we can implement them later //TODO: remember data for unknown states so we can implement them later
//TODO: log this //TODO: log this
$palette[] = $blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData()); $palette[] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData());
} }
}catch(NbtException | BlockStateDeserializeException $e){ }catch(NbtException | BlockStateDeserializeException $e){
throw new CorruptedChunkException("Invalid blockstate NBT at offset $i in paletted storage: " . $e->getMessage(), 0, $e); throw new CorruptedChunkException("Invalid blockstate NBT at offset $i in paletted storage: " . $e->getMessage(), 0, $e);
@ -188,7 +185,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
return PalettedBlockArray::fromData($bitsPerBlock, $words, $palette); return PalettedBlockArray::fromData($bitsPerBlock, $words, $palette);
} }
private static function serializeBlockPalette(BinaryStream $stream, PalettedBlockArray $blocks, BlockStateSerializer $blockStateSerializer) : void{ private function serializeBlockPalette(BinaryStream $stream, PalettedBlockArray $blocks) : void{
$stream->putByte($blocks->getBitsPerBlock() << 1); $stream->putByte($blocks->getBitsPerBlock() << 1);
$stream->put($blocks->getWordArray()); $stream->put($blocks->getWordArray());
@ -198,7 +195,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
} }
$tags = []; $tags = [];
foreach($palette as $p){ foreach($palette as $p){
$tags[] = new TreeRoot($blockStateSerializer->serialize($p)->toNbt()); $tags[] = new TreeRoot($this->blockStateSerializer->serialize($p)->toNbt());
} }
$stream->put((new LittleEndianNbtSerializer())->writeMultiple($tags)); $stream->put((new LittleEndianNbtSerializer())->writeMultiple($tags));
@ -327,8 +324,6 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
$binaryStream = new BinaryStream($extraRawData); $binaryStream = new BinaryStream($extraRawData);
$count = $binaryStream->getLInt(); $count = $binaryStream->getLInt();
$blockDataUpgrader = GlobalBlockStateHandlers::getUpgrader();
$blockStateDeserializer = GlobalBlockStateHandlers::getDeserializer();
for($i = 0; $i < $count; ++$i){ for($i = 0; $i < $count; ++$i){
$key = $binaryStream->getLInt(); $key = $binaryStream->getLInt();
$value = $binaryStream->getLShort(); $value = $binaryStream->getLShort();
@ -340,13 +335,13 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
$blockId = $value & 0xff; $blockId = $value & 0xff;
$blockData = ($value >> 8) & 0xf; $blockData = ($value >> 8) & 0xf;
$blockStateData = $blockDataUpgrader->upgradeIntIdMeta($blockId, $blockData); $blockStateData = $this->blockDataUpgrader->upgradeIntIdMeta($blockId, $blockData);
if($blockStateData === null){ if($blockStateData === null){
//TODO: we could preserve this in case it's supported in the future, but this was historically only //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 //used for grass anyway, so we probably don't need to care
continue; continue;
} }
$blockStateId = $blockStateDeserializer->deserialize($blockStateData); $blockStateId = $this->blockStateDeserializer->deserialize($blockStateData);
if(!isset($extraDataLayers[$ySub])){ if(!isset($extraDataLayers[$ySub])){
$extraDataLayers[$ySub] = new PalettedBlockArray(BlockTypeIds::AIR << Block::INTERNAL_STATE_DATA_BITS); $extraDataLayers[$ySub] = new PalettedBlockArray(BlockTypeIds::AIR << Block::INTERNAL_STATE_DATA_BITS);
@ -693,9 +688,6 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
if($chunk->getTerrainDirtyFlag(Chunk::DIRTY_FLAG_BLOCKS)){ if($chunk->getTerrainDirtyFlag(Chunk::DIRTY_FLAG_BLOCKS)){
$subChunks = $chunk->getSubChunks(); $subChunks = $chunk->getSubChunks();
//TODO: this should not rely on globals, but in PM4 we have no other option, and it's not worse than what we
//were doing before anyway ...
$blockStateSerializer = GlobalBlockStateHandlers::getSerializer();
foreach($subChunks as $y => $subChunk){ foreach($subChunks as $y => $subChunk){
$key = $index . ChunkDataKey::SUBCHUNK . chr($y); $key = $index . ChunkDataKey::SUBCHUNK . chr($y);
if($subChunk->isEmptyAuthoritative()){ if($subChunk->isEmptyAuthoritative()){
@ -707,7 +699,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
$layers = $subChunk->getBlockLayers(); $layers = $subChunk->getBlockLayers();
$subStream->putByte(count($layers)); $subStream->putByte(count($layers));
foreach($layers as $blocks){ foreach($layers as $blocks){
self::serializeBlockPalette($subStream, $blocks, $blockStateSerializer); $this->serializeBlockPalette($subStream, $blocks);
} }
$write->put($key, $subStream->getBuffer()); $write->put($key, $subStream->getBuffer());