mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-20 16:00:20 +00:00
WorldProvider subsystem no longer depends on Chunk
Instead, it provides the data needed to construct the chunk, which doesn't require the provider to be aware of anywhere near as much logic.
This commit is contained in:
parent
d57954dff0
commit
a49842278a
@ -1400,10 +1400,11 @@ class World implements ChunkManager{
|
||||
foreach($this->chunks as $chunkHash => $chunk){
|
||||
self::getXZ($chunkHash, $chunkX, $chunkZ);
|
||||
$this->provider->saveChunk($chunkX, $chunkZ, new ChunkData(
|
||||
$chunk,
|
||||
$chunk->getSubChunks(),
|
||||
$chunk->isPopulated(),
|
||||
array_map(fn(Entity $e) => $e->saveNBT(), array_filter($this->getChunkEntities($chunkX, $chunkZ), fn(Entity $e) => $e->canSaveWithChunk())),
|
||||
array_map(fn(Tile $t) => $t->saveNBT(), $chunk->getTiles()),
|
||||
));
|
||||
), $chunk->getTerrainDirtyFlags());
|
||||
$chunk->clearTerrainDirtyFlags();
|
||||
}
|
||||
}finally{
|
||||
@ -2717,7 +2718,8 @@ class World implements ChunkManager{
|
||||
return null;
|
||||
}
|
||||
|
||||
$chunk = $loadedChunkData->getData()->getChunk();
|
||||
$chunkData = $loadedChunkData->getData();
|
||||
$chunk = new Chunk($chunkData->getSubChunks(), $chunkData->isPopulated());
|
||||
if(!$loadedChunkData->isUpgraded()){
|
||||
$chunk->clearTerrainDirtyFlags();
|
||||
}else{
|
||||
@ -2726,7 +2728,7 @@ class World implements ChunkManager{
|
||||
$this->chunks[$chunkHash] = $chunk;
|
||||
unset($this->blockCache[$chunkHash]);
|
||||
|
||||
$this->initChunk($x, $z, $loadedChunkData->getData());
|
||||
$this->initChunk($x, $z, $chunkData);
|
||||
|
||||
(new ChunkLoadEvent($this, $x, $z, $this->chunks[$chunkHash], false))->call();
|
||||
|
||||
@ -2853,10 +2855,11 @@ class World implements ChunkManager{
|
||||
$this->timings->syncChunkSave->startTiming();
|
||||
try{
|
||||
$this->provider->saveChunk($x, $z, new ChunkData(
|
||||
$chunk,
|
||||
$chunk->getSubChunks(),
|
||||
$chunk->isPopulated(),
|
||||
array_map(fn(Entity $e) => $e->saveNBT(), array_filter($this->getChunkEntities($x, $z), fn(Entity $e) => $e->canSaveWithChunk())),
|
||||
array_map(fn(Tile $t) => $t->saveNBT(), $chunk->getTiles()),
|
||||
));
|
||||
), $chunk->getTerrainDirtyFlags());
|
||||
}finally{
|
||||
$this->timings->syncChunkSave->stopTiming();
|
||||
}
|
||||
|
@ -24,21 +24,28 @@ declare(strict_types=1);
|
||||
namespace pocketmine\world\format\io;
|
||||
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\world\format\Chunk;
|
||||
use pocketmine\world\format\SubChunk;
|
||||
|
||||
final class ChunkData{
|
||||
|
||||
/**
|
||||
* @param SubChunk[] $subChunks
|
||||
* @param CompoundTag[] $entityNBT
|
||||
* @param CompoundTag[] $tileNBT
|
||||
*/
|
||||
public function __construct(
|
||||
private Chunk $chunk,
|
||||
private array $subChunks,
|
||||
private bool $populated,
|
||||
private array $entityNBT,
|
||||
private array $tileNBT
|
||||
){}
|
||||
|
||||
public function getChunk() : Chunk{ return $this->chunk; }
|
||||
/**
|
||||
* @return SubChunk[]
|
||||
*/
|
||||
public function getSubChunks() : array{ return $this->subChunks; }
|
||||
|
||||
public function isPopulated() : bool{ return $this->populated; }
|
||||
|
||||
/** @return CompoundTag[] */
|
||||
public function getEntityNBT() : array{ return $this->entityNBT; }
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\world\format\io;
|
||||
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\world\format\Chunk;
|
||||
use pocketmine\world\generator\GeneratorManager;
|
||||
use pocketmine\world\generator\normal\Normal;
|
||||
use pocketmine\world\WorldCreationOptions;
|
||||
@ -142,9 +143,7 @@ class FormatConverter{
|
||||
$thisRound = $start;
|
||||
foreach($this->oldProvider->getAllChunks(true, $this->logger) as $coords => $loadedChunkData){
|
||||
[$chunkX, $chunkZ] = $coords;
|
||||
$chunkData = $loadedChunkData->getData();
|
||||
$chunkData->getChunk()->setTerrainDirty();
|
||||
$new->saveChunk($chunkX, $chunkZ, $chunkData);
|
||||
$new->saveChunk($chunkX, $chunkZ, $loadedChunkData->getData(), ~0);
|
||||
$counter++;
|
||||
if(($counter % $this->chunksPerProgressUpdate) === 0){
|
||||
$time = microtime(true);
|
||||
|
@ -27,5 +27,5 @@ interface WritableWorldProvider extends WorldProvider{
|
||||
/**
|
||||
* Saves a chunk (usually to disk).
|
||||
*/
|
||||
public function saveChunk(int $chunkX, int $chunkZ, ChunkData $chunkData) : void;
|
||||
public function saveChunk(int $chunkX, int $chunkZ, ChunkData $chunkData, int $dirtyFlags) : void;
|
||||
}
|
||||
|
@ -290,12 +290,15 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
|
||||
return $result;
|
||||
}
|
||||
|
||||
private static function serialize3dBiomes(BinaryStream $stream, Chunk $chunk) : void{
|
||||
/**
|
||||
* @param SubChunk[] $subChunks
|
||||
*/
|
||||
private static function serialize3dBiomes(BinaryStream $stream, array $subChunks) : void{
|
||||
//TODO: the server-side min/max may not coincide with the world storage min/max - we may need additional logic to handle this
|
||||
for($y = Chunk::MIN_SUBCHUNK_INDEX; $y <= Chunk::MAX_SUBCHUNK_INDEX; $y++){
|
||||
//TODO: is it worth trying to use the previous palette if it's the same as the current one? vanilla supports
|
||||
//this, but it's not clear if it's worth the effort to implement.
|
||||
self::serializeBiomePalette($stream, $chunk->getSubChunk($y)->getBiomeArray());
|
||||
self::serializeBiomePalette($stream, $subChunks[$y]->getBiomeArray());
|
||||
}
|
||||
}
|
||||
|
||||
@ -696,13 +699,13 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
|
||||
//TODO: tile ticks, biome states (?)
|
||||
|
||||
return new LoadedChunkData(
|
||||
data: new ChunkData(new Chunk($subChunks, $terrainPopulated), $entities, $tiles),
|
||||
data: new ChunkData($subChunks, $terrainPopulated, $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, int $dirtyFlags) : void{
|
||||
$index = LevelDB::chunkIndex($chunkX, $chunkZ);
|
||||
|
||||
$write = new \LevelDBWriteBatch();
|
||||
@ -710,10 +713,9 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
|
||||
$write->put($index . ChunkDataKey::NEW_VERSION, chr(self::CURRENT_LEVEL_CHUNK_VERSION));
|
||||
$write->put($index . ChunkDataKey::PM_DATA_VERSION, Binary::writeLLong(VersionInfo::WORLD_DATA_VERSION));
|
||||
|
||||
$chunk = $chunkData->getChunk();
|
||||
$subChunks = $chunkData->getSubChunks();
|
||||
|
||||
if($chunk->getTerrainDirtyFlag(Chunk::DIRTY_FLAG_BLOCKS)){
|
||||
$subChunks = $chunk->getSubChunks();
|
||||
if(($dirtyFlags & Chunk::DIRTY_FLAG_BLOCKS) !== 0){
|
||||
|
||||
foreach($subChunks as $y => $subChunk){
|
||||
$key = $index . ChunkDataKey::SUBCHUNK . chr($y);
|
||||
@ -734,16 +736,16 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
|
||||
}
|
||||
}
|
||||
|
||||
if($chunk->getTerrainDirtyFlag(Chunk::DIRTY_FLAG_BIOMES)){
|
||||
if(($dirtyFlags & Chunk::DIRTY_FLAG_BIOMES) !== 0){
|
||||
$write->delete($index . ChunkDataKey::HEIGHTMAP_AND_2D_BIOMES);
|
||||
$stream = new BinaryStream();
|
||||
$stream->put(str_repeat("\x00", 512)); //fake heightmap
|
||||
self::serialize3dBiomes($stream, $chunk);
|
||||
self::serialize3dBiomes($stream, $subChunks);
|
||||
$write->put($index . ChunkDataKey::HEIGHTMAP_AND_3D_BIOMES, $stream->getBuffer());
|
||||
}
|
||||
|
||||
//TODO: use this properly
|
||||
$write->put($index . ChunkDataKey::FINALIZATION, chr($chunk->isPopulated() ? self::FINALISATION_DONE : self::FINALISATION_NEEDS_POPULATION));
|
||||
$write->put($index . ChunkDataKey::FINALIZATION, chr($chunkData->isPopulated() ? self::FINALISATION_DONE : self::FINALISATION_NEEDS_POPULATION));
|
||||
|
||||
$this->writeTags($chunkData->getTileNBT(), $index . ChunkDataKey::BLOCK_ENTITIES, $write);
|
||||
$this->writeTags($chunkData->getEntityNBT(), $index . ChunkDataKey::ENTITIES, $write);
|
||||
|
@ -102,10 +102,8 @@ trait LegacyAnvilChunkTrait{
|
||||
|
||||
return new LoadedChunkData(
|
||||
data: new ChunkData(
|
||||
new Chunk(
|
||||
$subChunks,
|
||||
$chunk->getByte("TerrainPopulated", 0) !== 0
|
||||
),
|
||||
$subChunks,
|
||||
$chunk->getByte("TerrainPopulated", 0) !== 0,
|
||||
($entitiesTag = $chunk->getTag("Entities")) instanceof ListTag ? self::getCompoundList("Entities", $entitiesTag) : [],
|
||||
($tilesTag = $chunk->getTag("TileEntities")) instanceof ListTag ? self::getCompoundList("TileEntities", $tilesTag) : [],
|
||||
),
|
||||
|
@ -101,10 +101,8 @@ class McRegion extends RegionWorldProvider{
|
||||
|
||||
return new LoadedChunkData(
|
||||
data: new ChunkData(
|
||||
new Chunk(
|
||||
$subChunks,
|
||||
$chunk->getByte("TerrainPopulated", 0) !== 0
|
||||
),
|
||||
$subChunks,
|
||||
$chunk->getByte("TerrainPopulated", 0) !== 0,
|
||||
($entitiesTag = $chunk->getTag("Entities")) instanceof ListTag ? self::getCompoundList("Entities", $entitiesTag) : [],
|
||||
($tilesTag = $chunk->getTag("TileEntities")) instanceof ListTag ? self::getCompoundList("TileEntities", $tilesTag) : [],
|
||||
),
|
||||
|
@ -53,7 +53,7 @@ abstract class WritableRegionWorldProvider extends RegionWorldProvider implement
|
||||
|
||||
abstract protected function serializeChunk(ChunkData $chunk) : string;
|
||||
|
||||
public function saveChunk(int $chunkX, int $chunkZ, ChunkData $chunkData) : void{
|
||||
public function saveChunk(int $chunkX, int $chunkZ, ChunkData $chunkData, int $dirtyFlags) : void{
|
||||
self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ);
|
||||
$this->loadRegion($regionX, $regionZ)->writeChunk($chunkX & 0x1f, $chunkZ & 0x1f, $this->serializeChunk($chunkData));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user