Preparations for negative Y support

This commit is contained in:
Dylan K. Taylor 2021-11-08 17:28:22 +00:00
parent df39a1ca07
commit c6c992a1f0
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
8 changed files with 31 additions and 25 deletions

View File

@ -635,7 +635,7 @@ abstract class Entity{
$this->checkBlockIntersections(); $this->checkBlockIntersections();
if($this->location->y <= -16 and $this->isAlive()){ if($this->location->y <= World::Y_MIN - 16 and $this->isAlive()){
$ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_VOID, 10); $ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_VOID, 10);
$this->attack($ev); $this->attack($ev);
$hasUpdate = true; $hasUpdate = true;

View File

@ -46,8 +46,8 @@ final class ChunkSerializer{
* Chunks are sent in a stack, so every chunk below the top non-empty one must be sent. * Chunks are sent in a stack, so every chunk below the top non-empty one must be sent.
*/ */
public static function getSubChunkCount(Chunk $chunk) : int{ public static function getSubChunkCount(Chunk $chunk) : int{
for($count = count($chunk->getSubChunks()); $count > 0; --$count){ for($y = Chunk::MAX_SUBCHUNK_INDEX, $count = count($chunk->getSubChunks()); $y >= Chunk::MIN_SUBCHUNK_INDEX; --$y, --$count){
if($chunk->getSubChunk($count - 1)->isEmptyFast()){ if($chunk->getSubChunk($y)->isEmptyFast()){
continue; continue;
} }
return $count; return $count;
@ -59,7 +59,7 @@ final class ChunkSerializer{
public static function serializeFullChunk(Chunk $chunk, RuntimeBlockMapping $blockMapper, PacketSerializerContext $encoderContext, ?string $tiles = null) : string{ public static function serializeFullChunk(Chunk $chunk, RuntimeBlockMapping $blockMapper, PacketSerializerContext $encoderContext, ?string $tiles = null) : string{
$stream = PacketSerializer::encoder($encoderContext); $stream = PacketSerializer::encoder($encoderContext);
$subChunkCount = self::getSubChunkCount($chunk); $subChunkCount = self::getSubChunkCount($chunk);
for($y = 0; $y < $subChunkCount; ++$y){ for($y = Chunk::MIN_SUBCHUNK_INDEX, $writtenCount = 0; $writtenCount < $subChunkCount; ++$y, ++$writtenCount){
self::serializeSubChunk($chunk->getSubChunk($y), $blockMapper, $stream, false); self::serializeSubChunk($chunk->getSubChunk($y), $blockMapper, $stream, false);
} }
$stream->put($chunk->getBiomeIdArray()); $stream->put($chunk->getBiomeIdArray());

View File

@ -331,7 +331,7 @@ class World implements ChunkManager{
private const BLOCKHASH_Y_OFFSET = self::BLOCKHASH_Y_PADDING - self::Y_MIN; private const BLOCKHASH_Y_OFFSET = self::BLOCKHASH_Y_PADDING - self::Y_MIN;
private const BLOCKHASH_Y_MASK = (1 << self::BLOCKHASH_Y_BITS) - 1; private const BLOCKHASH_Y_MASK = (1 << self::BLOCKHASH_Y_BITS) - 1;
private const BLOCKHASH_XZ_MASK = (1 << self::MORTON3D_BIT_SIZE) - 1; private const BLOCKHASH_XZ_MASK = (1 << self::MORTON3D_BIT_SIZE) - 1;
private const BLOCKHASH_XZ_EXTRA_BITS = 6; private const BLOCKHASH_XZ_EXTRA_BITS = (self::MORTON3D_BIT_SIZE - self::BLOCKHASH_Y_BITS) >> 1;
private const BLOCKHASH_XZ_EXTRA_MASK = (1 << self::BLOCKHASH_XZ_EXTRA_BITS) - 1; private const BLOCKHASH_XZ_EXTRA_MASK = (1 << self::BLOCKHASH_XZ_EXTRA_BITS) - 1;
private const BLOCKHASH_XZ_SIGN_SHIFT = 64 - self::MORTON3D_BIT_SIZE - self::BLOCKHASH_XZ_EXTRA_BITS; private const BLOCKHASH_XZ_SIGN_SHIFT = 64 - self::MORTON3D_BIT_SIZE - self::BLOCKHASH_XZ_EXTRA_BITS;
private const BLOCKHASH_X_SHIFT = self::BLOCKHASH_Y_BITS; private const BLOCKHASH_X_SHIFT = self::BLOCKHASH_Y_BITS;

View File

@ -35,7 +35,9 @@ class Chunk{
public const DIRTY_FLAG_BLOCKS = 1 << 0; public const DIRTY_FLAG_BLOCKS = 1 << 0;
public const DIRTY_FLAG_BIOMES = 1 << 3; public const DIRTY_FLAG_BIOMES = 1 << 3;
public const MAX_SUBCHUNKS = 16; public const MIN_SUBCHUNK_INDEX = 0;
public const MAX_SUBCHUNK_INDEX = 15;
public const MAX_SUBCHUNKS = self::MAX_SUBCHUNK_INDEX - self::MIN_SUBCHUNK_INDEX + 1;
public const EDGE_LENGTH = SubChunk::EDGE_LENGTH; public const EDGE_LENGTH = SubChunk::EDGE_LENGTH;
public const COORD_BIT_SIZE = SubChunk::COORD_BIT_SIZE; public const COORD_BIT_SIZE = SubChunk::COORD_BIT_SIZE;
@ -71,10 +73,10 @@ class Chunk{
$this->subChunks = new \SplFixedArray(Chunk::MAX_SUBCHUNKS); $this->subChunks = new \SplFixedArray(Chunk::MAX_SUBCHUNKS);
foreach($this->subChunks as $y => $null){ foreach($this->subChunks as $y => $null){
$this->subChunks[$y] = $subChunks[$y] ?? new SubChunk(BlockLegacyIds::AIR << Block::INTERNAL_METADATA_BITS, []); $this->subChunks[$y] = $subChunks[$y + self::MIN_SUBCHUNK_INDEX] ?? new SubChunk(BlockLegacyIds::AIR << Block::INTERNAL_METADATA_BITS, []);
} }
$val = ($this->subChunks->getSize() * SubChunk::EDGE_LENGTH); $val = (self::MAX_SUBCHUNK_INDEX + 1) * SubChunk::EDGE_LENGTH;
$this->heightMap = HeightArray::fill($val); //TODO: what about lazily initializing this? $this->heightMap = HeightArray::fill($val); //TODO: what about lazily initializing this?
$this->biomeIds = $biomeIds; $this->biomeIds = $biomeIds;
@ -118,7 +120,7 @@ class Chunk{
* @return int|null 0-255, or null if there are no blocks in the column * @return int|null 0-255, or null if there are no blocks in the column
*/ */
public function getHighestBlockAt(int $x, int $z) : ?int{ public function getHighestBlockAt(int $x, int $z) : ?int{
for($y = $this->subChunks->count() - 1; $y >= 0; --$y){ for($y = self::MAX_SUBCHUNK_INDEX; $y >= self::MIN_SUBCHUNK_INDEX; --$y){
$height = $this->getSubChunk($y)->getHighestBlockAt($x, $z); $height = $this->getSubChunk($y)->getHighestBlockAt($x, $z);
if($height !== null){ if($height !== null){
return $height | ($y << SubChunk::COORD_BIT_SIZE); return $height | ($y << SubChunk::COORD_BIT_SIZE);
@ -280,21 +282,21 @@ class Chunk{
} }
public function getSubChunk(int $y) : SubChunk{ public function getSubChunk(int $y) : SubChunk{
if($y < 0 || $y >= $this->subChunks->getSize()){ if($y < self::MIN_SUBCHUNK_INDEX || $y > self::MAX_SUBCHUNK_INDEX){
throw new \InvalidArgumentException("Invalid subchunk Y coordinate $y"); throw new \InvalidArgumentException("Invalid subchunk Y coordinate $y");
} }
return $this->subChunks[$y]; return $this->subChunks[$y - self::MIN_SUBCHUNK_INDEX];
} }
/** /**
* Sets a subchunk in the chunk index * Sets a subchunk in the chunk index
*/ */
public function setSubChunk(int $y, ?SubChunk $subChunk) : void{ public function setSubChunk(int $y, ?SubChunk $subChunk) : void{
if($y < 0 or $y >= $this->subChunks->getSize()){ if($y < self::MIN_SUBCHUNK_INDEX or $y > self::MAX_SUBCHUNK_INDEX){
throw new \InvalidArgumentException("Invalid subchunk Y coordinate $y"); throw new \InvalidArgumentException("Invalid subchunk Y coordinate $y");
} }
$this->subChunks[$y] = $subChunk ?? new SubChunk(BlockLegacyIds::AIR << Block::INTERNAL_METADATA_BITS, []); $this->subChunks[$y - self::MIN_SUBCHUNK_INDEX] = $subChunk ?? new SubChunk(BlockLegacyIds::AIR << Block::INTERNAL_METADATA_BITS, []);
$this->setTerrainDirtyFlag(self::DIRTY_FLAG_BLOCKS, true); $this->setTerrainDirtyFlag(self::DIRTY_FLAG_BLOCKS, true);
} }
@ -303,7 +305,11 @@ class Chunk{
* @phpstan-return array<int, SubChunk> * @phpstan-return array<int, SubChunk>
*/ */
public function getSubChunks() : array{ public function getSubChunks() : array{
return $this->subChunks->toArray(); $result = [];
foreach($this->subChunks as $yOffset => $subChunk){
$result[$yOffset + self::MIN_SUBCHUNK_INDEX] = $subChunk;
}
return $result;
} }
/** /**

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\world\format\io; namespace pocketmine\world\format\io;
use pocketmine\utils\Binary;
use pocketmine\utils\BinaryStream; use pocketmine\utils\BinaryStream;
use pocketmine\world\format\BiomeArray; use pocketmine\world\format\BiomeArray;
use pocketmine\world\format\Chunk; use pocketmine\world\format\Chunk;
@ -96,7 +97,7 @@ final class FastChunkSerializer{
$count = $stream->getByte(); $count = $stream->getByte();
for($subCount = 0; $subCount < $count; ++$subCount){ for($subCount = 0; $subCount < $count; ++$subCount){
$y = $stream->getByte(); $y = Binary::signByte($stream->getByte());
$airBlockId = $stream->getInt(); $airBlockId = $stream->getInt();
/** @var PalettedBlockArray[] $layers */ /** @var PalettedBlockArray[] $layers */

View File

@ -266,7 +266,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
case 3: //MCPE 1.0 case 3: //MCPE 1.0
$convertedLegacyExtraData = $this->deserializeLegacyExtraData($index, $chunkVersion); $convertedLegacyExtraData = $this->deserializeLegacyExtraData($index, $chunkVersion);
for($y = 0; $y < Chunk::MAX_SUBCHUNKS; ++$y){ for($y = Chunk::MIN_SUBCHUNK_INDEX; $y <= Chunk::MAX_SUBCHUNK_INDEX; ++$y){
if(($data = $this->db->get($index . self::TAG_SUBCHUNK_PREFIX . chr($y))) === false){ if(($data = $this->db->get($index . self::TAG_SUBCHUNK_PREFIX . chr($y))) === false){
continue; continue;
} }

View File

@ -115,11 +115,10 @@ class SkyLightUpdate extends LightUpdate{
//have to avoid filling full light for any subchunk that contains a heightmap Y coordinate //have to avoid filling full light for any subchunk that contains a heightmap Y coordinate
$highestHeightMapPlusOne = max($chunk->getHeightMapArray()) + 1; $highestHeightMapPlusOne = max($chunk->getHeightMapArray()) + 1;
$lowestClearSubChunk = ($highestHeightMapPlusOne >> SubChunk::COORD_BIT_SIZE) + (($highestHeightMapPlusOne & SubChunk::COORD_MASK) !== 0 ? 1 : 0); $lowestClearSubChunk = ($highestHeightMapPlusOne >> SubChunk::COORD_BIT_SIZE) + (($highestHeightMapPlusOne & SubChunk::COORD_MASK) !== 0 ? 1 : 0);
$chunkHeight = count($chunk->getSubChunks()); for($y = Chunk::MIN_SUBCHUNK_INDEX; $y < $lowestClearSubChunk && $y <= Chunk::MAX_SUBCHUNK_INDEX; $y++){
for($y = 0; $y < $lowestClearSubChunk && $y < $chunkHeight; $y++){
$chunk->getSubChunk($y)->setBlockSkyLightArray(LightArray::fill(0)); $chunk->getSubChunk($y)->setBlockSkyLightArray(LightArray::fill(0));
} }
for($y = $lowestClearSubChunk, $yMax = $chunkHeight; $y < $yMax; $y++){ for($y = $lowestClearSubChunk; $y <= Chunk::MAX_SUBCHUNK_INDEX; $y++){
$chunk->getSubChunk($y)->setBlockSkyLightArray(LightArray::fill(15)); $chunk->getSubChunk($y)->setBlockSkyLightArray(LightArray::fill(15));
} }
@ -130,7 +129,7 @@ class SkyLightUpdate extends LightUpdate{
for($x = 0; $x < Chunk::EDGE_LENGTH; ++$x){ for($x = 0; $x < Chunk::EDGE_LENGTH; ++$x){
for($z = 0; $z < Chunk::EDGE_LENGTH; ++$z){ for($z = 0; $z < Chunk::EDGE_LENGTH; ++$z){
$currentHeight = $chunk->getHeightMap($x, $z); $currentHeight = $chunk->getHeightMap($x, $z);
$maxAdjacentHeight = 0; $maxAdjacentHeight = World::Y_MIN;
if($x !== 0){ if($x !== 0){
$maxAdjacentHeight = max($maxAdjacentHeight, $chunk->getHeightMap($x - 1, $z)); $maxAdjacentHeight = max($maxAdjacentHeight, $chunk->getHeightMap($x - 1, $z));
} }
@ -174,21 +173,21 @@ class SkyLightUpdate extends LightUpdate{
* @phpstan-param \SplFixedArray<bool> $directSkyLightBlockers * @phpstan-param \SplFixedArray<bool> $directSkyLightBlockers
*/ */
private static function recalculateHeightMap(Chunk $chunk, \SplFixedArray $directSkyLightBlockers) : HeightArray{ private static function recalculateHeightMap(Chunk $chunk, \SplFixedArray $directSkyLightBlockers) : HeightArray{
$maxSubChunkY = count($chunk->getSubChunks()) - 1; $maxSubChunkY = Chunk::MAX_SUBCHUNK_INDEX;
for(; $maxSubChunkY >= 0; $maxSubChunkY--){ for(; $maxSubChunkY >= Chunk::MIN_SUBCHUNK_INDEX; $maxSubChunkY--){
if(!$chunk->getSubChunk($maxSubChunkY)->isEmptyFast()){ if(!$chunk->getSubChunk($maxSubChunkY)->isEmptyFast()){
break; break;
} }
} }
$result = HeightArray::fill(World::Y_MIN); $result = HeightArray::fill(World::Y_MIN);
if($maxSubChunkY === -1){ //whole column is definitely empty if($maxSubChunkY < Chunk::MIN_SUBCHUNK_INDEX){ //whole column is definitely empty
return $result; return $result;
} }
for($z = 0; $z < Chunk::EDGE_LENGTH; ++$z){ for($z = 0; $z < Chunk::EDGE_LENGTH; ++$z){
for($x = 0; $x < Chunk::EDGE_LENGTH; ++$x){ for($x = 0; $x < Chunk::EDGE_LENGTH; ++$x){
$y = null; $y = null;
for($subChunkY = $maxSubChunkY; $subChunkY >= 0; $subChunkY--){ for($subChunkY = $maxSubChunkY; $subChunkY >= Chunk::MIN_SUBCHUNK_INDEX; $subChunkY--){
$subHighestBlockY = $chunk->getSubChunk($subChunkY)->getHighestBlockAt($x, $z); $subHighestBlockY = $chunk->getSubChunk($subChunkY)->getHighestBlockAt($x, $z);
if($subHighestBlockY !== null){ if($subHighestBlockY !== null){
$y = ($subChunkY * SubChunk::EDGE_LENGTH) + $subHighestBlockY; $y = ($subChunkY * SubChunk::EDGE_LENGTH) + $subHighestBlockY;

View File

@ -68,7 +68,7 @@ class SubChunkExplorer{
if($this->currentSubChunk === null or $this->currentY !== $newChunkY){ if($this->currentSubChunk === null or $this->currentY !== $newChunkY){
$this->currentY = $newChunkY; $this->currentY = $newChunkY;
if($this->currentY < 0 or $this->currentY >= $this->currentChunk->getHeight()){ if($this->currentY < Chunk::MIN_SUBCHUNK_INDEX or $this->currentY > Chunk::MAX_SUBCHUNK_INDEX){
$this->currentSubChunk = null; $this->currentSubChunk = null;
return SubChunkExplorerStatus::INVALID; return SubChunkExplorerStatus::INVALID;
} }