WorldProvider: allow loadChunk() to return additional information about the loaded chunk data

this will be needed for dealing with #5733. I don't plan to fix that before 5.0, but we need to make the appropriate BC breaks now, before release.
This commit is contained in:
Dylan K. Taylor 2023-05-29 17:03:39 +01:00
parent f5a1a0c9cb
commit c10be0f346
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
8 changed files with 91 additions and 30 deletions

View File

@ -2702,25 +2702,25 @@ class World implements ChunkManager{
$this->timings->syncChunkLoadData->startTiming(); $this->timings->syncChunkLoadData->startTiming();
$chunk = null; $loadedChunkData = null;
try{ try{
$chunk = $this->provider->loadChunk($x, $z); $loadedChunkData = $this->provider->loadChunk($x, $z);
}catch(CorruptedChunkException $e){ }catch(CorruptedChunkException $e){
$this->logger->critical("Failed to load chunk x=$x z=$z: " . $e->getMessage()); $this->logger->critical("Failed to load chunk x=$x z=$z: " . $e->getMessage());
} }
$this->timings->syncChunkLoadData->stopTiming(); $this->timings->syncChunkLoadData->stopTiming();
if($chunk === null){ if($loadedChunkData === null){
$this->timings->syncChunkLoad->stopTiming(); $this->timings->syncChunkLoad->stopTiming();
return null; return null;
} }
$this->chunks[$chunkHash] = $chunk->getChunk(); $this->chunks[$chunkHash] = $loadedChunkData->getData()->getChunk();
unset($this->blockCache[$chunkHash]); unset($this->blockCache[$chunkHash]);
$this->initChunk($x, $z, $chunk); $this->initChunk($x, $z, $loadedChunkData->getData());
(new ChunkLoadEvent($this, $x, $z, $this->chunks[$chunkHash], false))->call(); (new ChunkLoadEvent($this, $x, $z, $this->chunks[$chunkHash], false))->call();

View File

@ -140,10 +140,11 @@ class FormatConverter{
$start = microtime(true); $start = microtime(true);
$thisRound = $start; $thisRound = $start;
foreach($this->oldProvider->getAllChunks(true, $this->logger) as $coords => $chunk){ foreach($this->oldProvider->getAllChunks(true, $this->logger) as $coords => $loadedChunkData){
[$chunkX, $chunkZ] = $coords; [$chunkX, $chunkZ] = $coords;
$chunk->getChunk()->setTerrainDirty(); $chunkData = $loadedChunkData->getData();
$new->saveChunk($chunkX, $chunkZ, $chunk); $chunkData->getChunk()->setTerrainDirty();
$new->saveChunk($chunkX, $chunkZ, $chunkData);
$counter++; $counter++;
if(($counter % $this->chunksPerProgressUpdate) === 0){ if(($counter % $this->chunksPerProgressUpdate) === 0){
$time = microtime(true); $time = microtime(true);

View File

@ -0,0 +1,45 @@
<?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\world\format\io;
/**
* Encapsulates information returned when loading a chunk. This includes more information than saving a chunk, since the
* data might have been upgraded or need post-processing.
*/
final class LoadedChunkData{
public const FIXER_FLAG_NONE = 0;
public const FIXER_FLAG_ALL = ~0;
public function __construct(
private ChunkData $data,
private bool $upgraded,
private int $fixerFlags
){}
public function getData() : ChunkData{ return $this->data; }
public function isUpgraded() : bool{ return $this->upgraded; }
public function getFixerFlags() : int{ return $this->fixerFlags; }
}

View File

@ -43,7 +43,7 @@ interface WorldProvider{
* *
* @throws CorruptedChunkException * @throws CorruptedChunkException
*/ */
public function loadChunk(int $chunkX, int $chunkZ) : ?ChunkData; public function loadChunk(int $chunkX, int $chunkZ) : ?LoadedChunkData;
/** /**
* Performs garbage collection in the world provider, such as cleaning up regions in Region-based worlds. * Performs garbage collection in the world provider, such as cleaning up regions in Region-based worlds.
@ -63,8 +63,8 @@ interface WorldProvider{
/** /**
* Returns a generator which yields all the chunks in this world. * Returns a generator which yields all the chunks in this world.
* *
* @return \Generator|ChunkData[] * @return \Generator|LoadedChunkData[]
* @phpstan-return \Generator<array{int, int}, ChunkData, void, void> * @phpstan-return \Generator<array{int, int}, LoadedChunkData, void, void>
* @throws CorruptedChunkException * @throws CorruptedChunkException
*/ */
public function getAllChunks(bool $skipCorrupted = false, ?\Logger $logger = null) : \Generator; public function getAllChunks(bool $skipCorrupted = false, ?\Logger $logger = null) : \Generator;

View File

@ -44,6 +44,7 @@ use pocketmine\world\format\io\exception\CorruptedChunkException;
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\io\GlobalBlockStateHandlers; use pocketmine\world\format\io\GlobalBlockStateHandlers;
use pocketmine\world\format\io\LoadedChunkData;
use pocketmine\world\format\io\WorldData; use pocketmine\world\format\io\WorldData;
use pocketmine\world\format\io\WritableWorldProvider; use pocketmine\world\format\io\WritableWorldProvider;
use pocketmine\world\format\PalettedBlockArray; use pocketmine\world\format\PalettedBlockArray;
@ -593,7 +594,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
/** /**
* @throws CorruptedChunkException * @throws CorruptedChunkException
*/ */
public function loadChunk(int $chunkX, int $chunkZ) : ?ChunkData{ public function loadChunk(int $chunkX, int $chunkZ) : ?LoadedChunkData{
$index = LevelDB::chunkIndex($chunkX, $chunkZ); $index = LevelDB::chunkIndex($chunkX, $chunkZ);
$chunkVersion = $this->readVersion($chunkX, $chunkZ); $chunkVersion = $this->readVersion($chunkX, $chunkZ);
@ -704,7 +705,11 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
$chunk->setTerrainDirty(); //trigger rewriting chunk to disk if it was converted from an older format $chunk->setTerrainDirty(); //trigger rewriting chunk to disk if it was converted from an older format
} }
return new ChunkData($chunk, $entities, $tiles); return new LoadedChunkData(
data: new ChunkData($chunk, $entities, $tiles),
upgraded: $hasBeenUpgraded,
fixerFlags: LoadedChunkData::FIXER_FLAG_ALL //TODO: fill this by version rather than just setting all flags
);
} }
public function saveChunk(int $chunkX, int $chunkZ, ChunkData $chunkData) : void{ public function saveChunk(int $chunkX, int $chunkZ, ChunkData $chunkData) : void{

View File

@ -36,6 +36,7 @@ use pocketmine\world\format\Chunk;
use pocketmine\world\format\io\ChunkData; use pocketmine\world\format\io\ChunkData;
use pocketmine\world\format\io\ChunkUtils; use pocketmine\world\format\io\ChunkUtils;
use pocketmine\world\format\io\exception\CorruptedChunkException; use pocketmine\world\format\io\exception\CorruptedChunkException;
use pocketmine\world\format\io\LoadedChunkData;
use pocketmine\world\format\PalettedBlockArray; use pocketmine\world\format\PalettedBlockArray;
use pocketmine\world\format\SubChunk; use pocketmine\world\format\SubChunk;
use function strlen; use function strlen;
@ -54,7 +55,7 @@ trait LegacyAnvilChunkTrait{
/** /**
* @throws CorruptedChunkException * @throws CorruptedChunkException
*/ */
protected function deserializeChunk(string $data) : ?ChunkData{ protected function deserializeChunk(string $data) : ?LoadedChunkData{
$decompressed = @zlib_decode($data); $decompressed = @zlib_decode($data);
if($decompressed === false){ if($decompressed === false){
throw new CorruptedChunkException("Failed to decompress chunk NBT"); throw new CorruptedChunkException("Failed to decompress chunk NBT");
@ -99,13 +100,17 @@ trait LegacyAnvilChunkTrait{
} }
} }
return new ChunkData( return new LoadedChunkData(
data: new ChunkData(
new Chunk( new Chunk(
$subChunks, $subChunks,
$chunk->getByte("TerrainPopulated", 0) !== 0 $chunk->getByte("TerrainPopulated", 0) !== 0
), ),
($entitiesTag = $chunk->getTag("Entities")) instanceof ListTag ? self::getCompoundList("Entities", $entitiesTag) : [], ($entitiesTag = $chunk->getTag("Entities")) instanceof ListTag ? self::getCompoundList("Entities", $entitiesTag) : [],
($tilesTag = $chunk->getTag("TileEntities")) instanceof ListTag ? self::getCompoundList("TileEntities", $tilesTag) : [], ($tilesTag = $chunk->getTag("TileEntities")) instanceof ListTag ? self::getCompoundList("TileEntities", $tilesTag) : [],
),
upgraded: true,
fixerFlags: LoadedChunkData::FIXER_FLAG_ALL
); );
} }

View File

@ -37,6 +37,7 @@ use pocketmine\world\format\Chunk;
use pocketmine\world\format\io\ChunkData; use pocketmine\world\format\io\ChunkData;
use pocketmine\world\format\io\ChunkUtils; use pocketmine\world\format\io\ChunkUtils;
use pocketmine\world\format\io\exception\CorruptedChunkException; use pocketmine\world\format\io\exception\CorruptedChunkException;
use pocketmine\world\format\io\LoadedChunkData;
use pocketmine\world\format\PalettedBlockArray; use pocketmine\world\format\PalettedBlockArray;
use pocketmine\world\format\SubChunk; use pocketmine\world\format\SubChunk;
use function strlen; use function strlen;
@ -46,7 +47,7 @@ class McRegion extends RegionWorldProvider{
/** /**
* @throws CorruptedChunkException * @throws CorruptedChunkException
*/ */
protected function deserializeChunk(string $data) : ?ChunkData{ protected function deserializeChunk(string $data) : ?LoadedChunkData{
$decompressed = @zlib_decode($data); $decompressed = @zlib_decode($data);
if($decompressed === false){ if($decompressed === false){
throw new CorruptedChunkException("Failed to decompress chunk NBT"); throw new CorruptedChunkException("Failed to decompress chunk NBT");
@ -98,13 +99,17 @@ class McRegion extends RegionWorldProvider{
} }
} }
return new ChunkData( return new LoadedChunkData(
data: new ChunkData(
new Chunk( new Chunk(
$subChunks, $subChunks,
$chunk->getByte("TerrainPopulated", 0) !== 0 $chunk->getByte("TerrainPopulated", 0) !== 0
), ),
($entitiesTag = $chunk->getTag("Entities")) instanceof ListTag ? self::getCompoundList("Entities", $entitiesTag) : [], ($entitiesTag = $chunk->getTag("Entities")) instanceof ListTag ? self::getCompoundList("Entities", $entitiesTag) : [],
($tilesTag = $chunk->getTag("TileEntities")) instanceof ListTag ? self::getCompoundList("TileEntities", $tilesTag) : [], ($tilesTag = $chunk->getTag("TileEntities")) instanceof ListTag ? self::getCompoundList("TileEntities", $tilesTag) : [],
),
upgraded: true,
fixerFlags: LoadedChunkData::FIXER_FLAG_ALL
); );
} }

View File

@ -28,9 +28,9 @@ use pocketmine\nbt\tag\ByteArrayTag;
use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\ListTag;
use pocketmine\world\format\io\BaseWorldProvider; use pocketmine\world\format\io\BaseWorldProvider;
use pocketmine\world\format\io\ChunkData;
use pocketmine\world\format\io\data\JavaWorldData; use pocketmine\world\format\io\data\JavaWorldData;
use pocketmine\world\format\io\exception\CorruptedChunkException; use pocketmine\world\format\io\exception\CorruptedChunkException;
use pocketmine\world\format\io\LoadedChunkData;
use pocketmine\world\format\io\WorldData; use pocketmine\world\format\io\WorldData;
use Symfony\Component\Filesystem\Path; use Symfony\Component\Filesystem\Path;
use function assert; use function assert;
@ -147,7 +147,7 @@ abstract class RegionWorldProvider extends BaseWorldProvider{
/** /**
* @throws CorruptedChunkException * @throws CorruptedChunkException
*/ */
abstract protected function deserializeChunk(string $data) : ?ChunkData; abstract protected function deserializeChunk(string $data) : ?LoadedChunkData;
/** /**
* @return CompoundTag[] * @return CompoundTag[]
@ -189,7 +189,7 @@ abstract class RegionWorldProvider extends BaseWorldProvider{
/** /**
* @throws CorruptedChunkException * @throws CorruptedChunkException
*/ */
public function loadChunk(int $chunkX, int $chunkZ) : ?ChunkData{ public function loadChunk(int $chunkX, int $chunkZ) : ?LoadedChunkData{
$regionX = $regionZ = null; $regionX = $regionZ = null;
self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ); self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ);
assert(is_int($regionX) && is_int($regionZ)); assert(is_int($regionX) && is_int($regionZ));