Stop hardcoding chunk dimensions everywhere (#4443)

This commit is contained in:
Dylan T 2021-09-10 16:13:25 +01:00 committed by GitHub
parent 9d5a86fe53
commit 4111d92b98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 166 additions and 140 deletions

View File

@ -100,6 +100,7 @@ use pocketmine\utils\Promise;
use pocketmine\utils\Terminal;
use pocketmine\utils\TextFormat;
use pocketmine\utils\Utils;
use pocketmine\world\format\Chunk;
use pocketmine\world\format\io\WorldProviderManager;
use pocketmine\world\format\io\WritableWorldProviderManagerEntry;
use pocketmine\world\generator\Generator;
@ -546,7 +547,7 @@ class Server{
$spawn = $world->getSpawnLocation();
}
$playerPromise = new Promise();
$world->requestChunkPopulation($spawn->getFloorX() >> 4, $spawn->getFloorZ() >> 4, null)->onCompletion(
$world->requestChunkPopulation($spawn->getFloorX() >> Chunk::COORD_BIT_SIZE, $spawn->getFloorZ() >> Chunk::COORD_BIT_SIZE, null)->onCompletion(
function() use ($playerPromise, $class, $session, $playerInfo, $authenticated, $world, $playerPos, $spawn, $offlinePlayerData) : void{
if(!$session->isConnected()){
$playerPromise->reject();

View File

@ -40,6 +40,7 @@ use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use pocketmine\world\format\Chunk;
use pocketmine\world\Position;
use pocketmine\world\World;
use function assert;
@ -142,7 +143,7 @@ class Block{
}
public function writeStateToWorld() : void{
$this->position->getWorld()->getOrLoadChunkAtPosition($this->position)->setFullBlock($this->position->x & 0xf, $this->position->y, $this->position->z & 0xf, $this->getFullId());
$this->position->getWorld()->getOrLoadChunkAtPosition($this->position)->setFullBlock($this->position->x & Chunk::COORD_MASK, $this->position->y, $this->position->z & Chunk::COORD_MASK, $this->getFullId());
$tileType = $this->idInfo->getTileClass();
$oldTile = $this->position->getWorld()->getTile($this->position);

View File

@ -28,6 +28,7 @@ use pocketmine\block\inventory\DoubleChestInventory;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\world\format\Chunk;
use pocketmine\world\World;
use function abs;
@ -99,7 +100,7 @@ class Chest extends Spawnable implements Container, Nameable{
$this->inventory->removeAllViewers();
if($this->doubleInventory !== null){
if($this->isPaired() and $this->position->getWorld()->isChunkLoaded($this->pairX >> 4, $this->pairZ >> 4)){
if($this->isPaired() and $this->position->getWorld()->isChunkLoaded($this->pairX >> Chunk::COORD_BIT_SIZE, $this->pairZ >> Chunk::COORD_BIT_SIZE)){
$this->doubleInventory->removeAllViewers();
if(($pair = $this->getPair()) !== null){
$pair->doubleInventory = null;

View File

@ -56,6 +56,7 @@ use pocketmine\player\Player;
use pocketmine\Server;
use pocketmine\timings\Timings;
use pocketmine\timings\TimingsHandler;
use pocketmine\world\format\Chunk;
use pocketmine\world\Position;
use pocketmine\world\sound\Sound;
use pocketmine\world\World;
@ -1436,7 +1437,7 @@ abstract class Entity{
//TODO: this will cause some visible lag during chunk resends; if the player uses a spawn egg in a chunk, the
//created entity won't be visible until after the resend arrives. However, this is better than possibly crashing
//the player by sending them entities too early.
if(!isset($this->hasSpawned[$id]) and $player->hasReceivedChunk($this->location->getFloorX() >> 4, $this->location->getFloorZ() >> 4)){
if(!isset($this->hasSpawned[$id]) and $player->hasReceivedChunk($this->location->getFloorX() >> Chunk::COORD_BIT_SIZE, $this->location->getFloorZ() >> Chunk::COORD_BIT_SIZE)){
$this->hasSpawned[$id] = $player;
$this->sendSpawnPacket($player);

View File

@ -193,7 +193,7 @@ class ChunkCache implements ChunkListener{
public function onBlockChanged(Vector3 $block) : void{
//FIXME: requesters will still receive this chunk after it's been dropped, but we can't mark this for a simple
//sync here because it can spam the worker pool
$this->destroy($block->getFloorX() >> 4, $block->getFloorZ() >> 4);
$this->destroy($block->getFloorX() >> Chunk::COORD_BIT_SIZE, $block->getFloorZ() >> Chunk::COORD_BIT_SIZE);
}
/**

View File

@ -272,9 +272,9 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
$world = $spawnLocation->getWorld();
//load the spawn chunk so we can see the terrain
$world->registerChunkLoader($this->chunkLoader, $spawnLocation->getFloorX() >> 4, $spawnLocation->getFloorZ() >> 4, true);
$world->registerChunkListener($this, $spawnLocation->getFloorX() >> 4, $spawnLocation->getFloorZ() >> 4);
$this->usedChunks[World::chunkHash($spawnLocation->getFloorX() >> 4, $spawnLocation->getFloorZ() >> 4)] = UsedChunkStatus::NEEDED();
$world->registerChunkLoader($this->chunkLoader, $spawnLocation->getFloorX() >> Chunk::COORD_BIT_SIZE, $spawnLocation->getFloorZ() >> Chunk::COORD_BIT_SIZE, true);
$world->registerChunkListener($this, $spawnLocation->getFloorX() >> Chunk::COORD_BIT_SIZE, $spawnLocation->getFloorZ() >> Chunk::COORD_BIT_SIZE);
$this->usedChunks[World::chunkHash($spawnLocation->getFloorX() >> Chunk::COORD_BIT_SIZE, $spawnLocation->getFloorZ() >> Chunk::COORD_BIT_SIZE)] = UsedChunkStatus::NEEDED();
parent::__construct($spawnLocation, $this->playerInfo->getSkin(), $namedtag);
@ -794,8 +794,8 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
foreach($this->chunkSelector->selectChunks(
$this->server->getAllowedViewDistance($this->viewDistance),
$this->location->getFloorX() >> 4,
$this->location->getFloorZ() >> 4
$this->location->getFloorX() >> Chunk::COORD_BIT_SIZE,
$this->location->getFloorZ() >> Chunk::COORD_BIT_SIZE
) as $hash){
if(!isset($this->usedChunks[$hash]) or $this->usedChunks[$hash]->equals(UsedChunkStatus::NEEDED())){
$newOrder[$hash] = true;
@ -1126,7 +1126,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
$this->logger->debug("Moved too fast, reverting movement");
$this->logger->debug("Old position: " . $this->location->asVector3() . ", new position: " . $newPos);
$revert = true;
}elseif(!$this->getWorld()->isInLoadedTerrain($newPos) or !$this->getWorld()->isChunkGenerated($newPos->getFloorX() >> 4, $newPos->getFloorZ() >> 4)){
}elseif(!$this->getWorld()->isInLoadedTerrain($newPos) or !$this->getWorld()->isChunkGenerated($newPos->getFloorX() >> Chunk::COORD_BIT_SIZE, $newPos->getFloorZ() >> Chunk::COORD_BIT_SIZE)){
$revert = true;
$this->nextChunkOrderRun = 0;
}
@ -2128,7 +2128,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
$this->logger->debug("Waiting for spawn terrain generation for respawn");
$spawn = $this->getSpawn();
$spawn->getWorld()->orderChunkPopulation($spawn->getFloorX() >> 4, $spawn->getFloorZ() >> 4, null)->onCompletion(
$spawn->getWorld()->orderChunkPopulation($spawn->getFloorX() >> Chunk::COORD_BIT_SIZE, $spawn->getFloorZ() >> Chunk::COORD_BIT_SIZE, null)->onCompletion(
function() use ($spawn) : void{
if(!$this->isConnected()){
return;

View File

@ -37,6 +37,7 @@ use pocketmine\item\ItemFactory;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\world\format\SubChunk;
use pocketmine\world\particle\HugeExplodeSeedParticle;
use pocketmine\world\sound\ExplodeSound;
use pocketmine\world\utils\SubChunkExplorer;
@ -128,7 +129,7 @@ class Explosion{
continue;
}
$state = $this->subChunkExplorer->currentSubChunk->getFullBlock($vBlockX & 0x0f, $vBlockY & 0x0f, $vBlockZ & 0x0f);
$state = $this->subChunkExplorer->currentSubChunk->getFullBlock($vBlockX & SubChunk::COORD_MASK, $vBlockY & SubChunk::COORD_MASK, $vBlockZ & SubChunk::COORD_MASK);
$blastResistance = $blockFactory->blastResistance[$state];
if($blastResistance >= 0){

View File

@ -45,15 +45,15 @@ class SimpleChunkManager implements ChunkManager{
}
public function getBlockAt(int $x, int $y, int $z) : Block{
if($this->isInWorld($x, $y, $z) && ($chunk = $this->getChunk($x >> 4, $z >> 4)) !== null){
return BlockFactory::getInstance()->fromFullBlock($chunk->getFullBlock($x & 0xf, $y, $z & 0xf));
if($this->isInWorld($x, $y, $z) && ($chunk = $this->getChunk($x >> Chunk::COORD_BIT_SIZE, $z >> Chunk::COORD_BIT_SIZE)) !== null){
return BlockFactory::getInstance()->fromFullBlock($chunk->getFullBlock($x & Chunk::COORD_MASK, $y, $z & Chunk::COORD_MASK));
}
return VanillaBlocks::AIR();
}
public function setBlockAt(int $x, int $y, int $z, Block $block) : void{
if(($chunk = $this->getChunk($x >> 4, $z >> 4)) !== null){
$chunk->setFullBlock($x & 0xf, $y, $z & 0xf, $block->getFullId());
if(($chunk = $this->getChunk($x >> Chunk::COORD_BIT_SIZE, $z >> Chunk::COORD_BIT_SIZE)) !== null){
$chunk->setFullBlock($x & Chunk::COORD_MASK, $y, $z & Chunk::COORD_MASK, $block->getFullId());
}else{
throw new \InvalidArgumentException("Cannot set block at coordinates x=$x,y=$y,z=$z, terrain is not loaded or out of bounds");
}

View File

@ -79,6 +79,7 @@ use pocketmine\world\format\io\ChunkData;
use pocketmine\world\format\io\exception\CorruptedChunkException;
use pocketmine\world\format\io\WritableWorldProvider;
use pocketmine\world\format\LightArray;
use pocketmine\world\format\SubChunk;
use pocketmine\world\generator\GeneratorManager;
use pocketmine\world\generator\GeneratorRegisterTask;
use pocketmine\world\generator\GeneratorUnregisterTask;
@ -625,14 +626,14 @@ class World implements ChunkManager{
* @return Player[]
*/
public function getViewersForPosition(Vector3 $pos) : array{
return $this->getChunkPlayers($pos->getFloorX() >> 4, $pos->getFloorZ() >> 4);
return $this->getChunkPlayers($pos->getFloorX() >> Chunk::COORD_BIT_SIZE, $pos->getFloorZ() >> Chunk::COORD_BIT_SIZE);
}
/**
* Broadcasts a packet to every player who has the target position within their view distance.
*/
public function broadcastPacketToViewers(Vector3 $pos, ClientboundPacket $packet) : void{
$this->broadcastPacketToPlayersUsingChunk($pos->getFloorX() >> 4, $pos->getFloorZ() >> 4, $packet);
$this->broadcastPacketToPlayersUsingChunk($pos->getFloorX() >> Chunk::COORD_BIT_SIZE, $pos->getFloorZ() >> Chunk::COORD_BIT_SIZE, $packet);
}
private function broadcastPacketToPlayersUsingChunk(int $chunkX, int $chunkZ, ClientboundPacket $packet) : void{
@ -827,7 +828,7 @@ class World implements ChunkManager{
$index = $this->neighbourBlockUpdateQueue->dequeue();
unset($this->neighbourBlockUpdateQueueIndex[$index]);
World::getBlockXYZ($index, $x, $y, $z);
if(!$this->isChunkLoaded($x >> 4, $z >> 4)){
if(!$this->isChunkLoaded($x >> Chunk::COORD_BIT_SIZE, $z >> Chunk::COORD_BIT_SIZE)){
continue;
}
@ -1008,8 +1009,8 @@ class World implements ChunkManager{
$randRange = (int) ($randRange > $this->chunkTickRadius ? $this->chunkTickRadius : $randRange);
foreach($this->tickingLoaders as $loader){
$chunkX = (int) floor($loader->getX()) >> 4;
$chunkZ = (int) floor($loader->getZ()) >> 4;
$chunkX = (int) floor($loader->getX()) >> Chunk::COORD_BIT_SIZE;
$chunkZ = (int) floor($loader->getZ()) >> Chunk::COORD_BIT_SIZE;
for($chunk = 0; $chunk < $chunksPerLoader; ++$chunk){
$dx = mt_rand(-$randRange, $randRange);
@ -1103,17 +1104,17 @@ class World implements ChunkManager{
//60 bits will be used by 5 blocks (12 bits each)
$k = mt_rand(0, (1 << 60) - 1);
}
$x = $k & 0x0f;
$y = ($k >> 4) & 0x0f;
$z = ($k >> 8) & 0x0f;
$k >>= 12;
$x = $k & SubChunk::COORD_MASK;
$y = ($k >> SubChunk::COORD_BIT_SIZE) & SubChunk::COORD_MASK;
$z = ($k >> (SubChunk::COORD_BIT_SIZE * 2)) & SubChunk::COORD_MASK;
$k >>= (SubChunk::COORD_BIT_SIZE * 3);
$state = $subChunk->getFullBlock($x, $y, $z);
if(isset($this->randomTickBlocks[$state])){
/** @var Block $block */
$block = BlockFactory::getInstance()->fromFullBlock($state);
$block->position($this, $chunkX * 16 + $x, ($Y << 4) + $y, $chunkZ * 16 + $z);
$block->position($this, $chunkX * Chunk::EDGE_LENGTH + $x, ($Y << SubChunk::COORD_BIT_SIZE) + $y, $chunkZ * Chunk::EDGE_LENGTH + $z);
$block->onRandomTick();
}
}
@ -1369,8 +1370,8 @@ class World implements ChunkManager{
if(!$this->isInWorld($x, $y, $z)){
return $y >= self::Y_MAX ? 15 : 0;
}
if(($chunk = $this->getChunk($x >> 4, $z >> 4)) !== null && $chunk->isLightPopulated() === true){
return $chunk->getSubChunk($y >> 4)->getBlockSkyLightArray()->get($x & 0x0f, $y & 0xf, $z & 0x0f);
if(($chunk = $this->getChunk($x >> Chunk::COORD_BIT_SIZE, $z >> Chunk::COORD_BIT_SIZE)) !== null && $chunk->isLightPopulated() === true){
return $chunk->getSubChunk($y >> Chunk::COORD_BIT_SIZE)->getBlockSkyLightArray()->get($x & SubChunk::COORD_MASK, $y & SubChunk::COORD_MASK, $z & SubChunk::COORD_MASK);
}
return 0; //TODO: this should probably throw instead (light not calculated yet)
}
@ -1394,14 +1395,14 @@ class World implements ChunkManager{
if(!$this->isInWorld($x, $y, $z)){
return 0;
}
if(($chunk = $this->getChunk($x >> 4, $z >> 4)) !== null && $chunk->isLightPopulated() === true){
return $chunk->getSubChunk($y >> 4)->getBlockLightArray()->get($x & 0x0f, $y & 0xf, $z & 0x0f);
if(($chunk = $this->getChunk($x >> Chunk::COORD_BIT_SIZE, $z >> Chunk::COORD_BIT_SIZE)) !== null && $chunk->isLightPopulated() === true){
return $chunk->getSubChunk($y >> Chunk::COORD_BIT_SIZE)->getBlockLightArray()->get($x & SubChunk::COORD_MASK, $y & SubChunk::COORD_MASK, $z & SubChunk::COORD_MASK);
}
return 0; //TODO: this should probably throw instead (light not calculated yet)
}
public function updateAllLight(int $x, int $y, int $z) : void{
if(($chunk = $this->getChunk($x >> 4, $z >> 4)) === null || $chunk->isLightPopulated() !== true){
if(($chunk = $this->getChunk($x >> Chunk::COORD_BIT_SIZE, $z >> Chunk::COORD_BIT_SIZE)) === null || $chunk->isLightPopulated() !== true){
$this->logger->debug("Skipped runtime light update of x=$x,y=$y,z=$z because the target area has not received base light calculation");
return;
}
@ -1437,7 +1438,7 @@ class World implements ChunkManager{
] as [$x1, $y1, $z1]){
if(
!$this->isInWorld($x1, $y1, $z1) ||
($chunk = $this->getChunk($x1 >> 4, $z1 >> 4)) === null ||
($chunk = $this->getChunk($x1 >> Chunk::COORD_BIT_SIZE, $z1 >> Chunk::COORD_BIT_SIZE)) === null ||
$chunk->isLightPopulated() !== true
){
continue;
@ -1518,7 +1519,7 @@ class World implements ChunkManager{
*/
public function getBlockAt(int $x, int $y, int $z, bool $cached = true, bool $addToCache = true) : Block{
$relativeBlockHash = null;
$chunkHash = World::chunkHash($x >> 4, $z >> 4);
$chunkHash = World::chunkHash($x >> Chunk::COORD_BIT_SIZE, $z >> Chunk::COORD_BIT_SIZE);
if($this->isInWorld($x, $y, $z)){
$relativeBlockHash = World::chunkBlockHash($x, $y, $z);
@ -1529,7 +1530,7 @@ class World implements ChunkManager{
$chunk = $this->chunks[$chunkHash] ?? null;
if($chunk !== null){
$block = BlockFactory::getInstance()->fromFullBlock($chunk->getFullBlock($x & 0x0f, $y, $z & 0x0f));
$block = BlockFactory::getInstance()->fromFullBlock($chunk->getFullBlock($x & Chunk::COORD_MASK, $y, $z & Chunk::COORD_MASK));
}else{
$addToCache = false;
$block = VanillaBlocks::AIR();
@ -1581,8 +1582,8 @@ class World implements ChunkManager{
if(!$this->isInWorld($x, $y, $z)){
throw new \InvalidArgumentException("Pos x=$x,y=$y,z=$z is outside of the world bounds");
}
$chunkX = $x >> 4;
$chunkZ = $z >> 4;
$chunkX = $x >> Chunk::COORD_BIT_SIZE;
$chunkZ = $z >> Chunk::COORD_BIT_SIZE;
if($this->isChunkLocked($chunkX, $chunkZ)){
throw new WorldException("Terrain is locked for generation/population");
}
@ -1600,7 +1601,7 @@ class World implements ChunkManager{
$block->writeStateToWorld();
$pos = $block->getPosition();
$chunkHash = World::chunkHash($x >> 4, $z >> 4);
$chunkHash = World::chunkHash($x >> Chunk::COORD_BIT_SIZE, $z >> Chunk::COORD_BIT_SIZE);
$relativeBlockHash = World::chunkBlockHash($x, $y, $z);
unset($this->blockCache[$chunkHash][$relativeBlockHash]);
@ -1610,7 +1611,7 @@ class World implements ChunkManager{
}
$this->changedBlocks[$chunkHash][$relativeBlockHash] = $pos;
foreach($this->getChunkListeners($x >> 4, $z >> 4) as $listener){
foreach($this->getChunkListeners($x >> Chunk::COORD_BIT_SIZE, $z >> Chunk::COORD_BIT_SIZE) as $listener){
$listener->onBlockChanged($pos);
}
@ -1671,8 +1672,8 @@ class World implements ChunkManager{
public function useBreakOn(Vector3 $vector, Item &$item = null, ?Player $player = null, bool $createParticles = false) : bool{
$vector = $vector->floor();
$chunkX = $vector->getFloorX() >> 4;
$chunkZ = $vector->getFloorZ() >> 4;
$chunkX = $vector->getFloorX() >> Chunk::COORD_BIT_SIZE;
$chunkZ = $vector->getFloorZ() >> Chunk::COORD_BIT_SIZE;
if(!$this->isChunkLoaded($chunkX, $chunkZ) || !$this->isChunkGenerated($chunkX, $chunkZ) || $this->isChunkLocked($chunkX, $chunkZ)){
return false;
}
@ -1782,8 +1783,8 @@ class World implements ChunkManager{
//TODO: build height limit messages for custom world heights and mcregion cap
return false;
}
$chunkX = $blockReplace->getPosition()->getFloorX() >> 4;
$chunkZ = $blockReplace->getPosition()->getFloorZ() >> 4;
$chunkX = $blockReplace->getPosition()->getFloorX() >> Chunk::COORD_BIT_SIZE;
$chunkZ = $blockReplace->getPosition()->getFloorZ() >> Chunk::COORD_BIT_SIZE;
if(!$this->isChunkLoaded($chunkX, $chunkZ) || !$this->isChunkGenerated($chunkX, $chunkZ) || $this->isChunkLocked($chunkX, $chunkZ)){
return false;
}
@ -1932,10 +1933,10 @@ class World implements ChunkManager{
public function getNearbyEntities(AxisAlignedBB $bb, ?Entity $entity = null) : array{
$nearby = [];
$minX = ((int) floor($bb->minX - 2)) >> 4;
$maxX = ((int) floor($bb->maxX + 2)) >> 4;
$minZ = ((int) floor($bb->minZ - 2)) >> 4;
$maxZ = ((int) floor($bb->maxZ + 2)) >> 4;
$minX = ((int) floor($bb->minX - 2)) >> Chunk::COORD_BIT_SIZE;
$maxX = ((int) floor($bb->maxX + 2)) >> Chunk::COORD_BIT_SIZE;
$minZ = ((int) floor($bb->minZ - 2)) >> Chunk::COORD_BIT_SIZE;
$maxZ = ((int) floor($bb->maxZ + 2)) >> Chunk::COORD_BIT_SIZE;
for($x = $minX; $x <= $maxX; ++$x){
for($z = $minZ; $z <= $maxZ; ++$z){
@ -1967,10 +1968,10 @@ class World implements ChunkManager{
public function getNearestEntity(Vector3 $pos, float $maxDistance, string $entityType = Entity::class, bool $includeDead = false) : ?Entity{
assert(is_a($entityType, Entity::class, true));
$minX = ((int) floor($pos->x - $maxDistance)) >> 4;
$maxX = ((int) floor($pos->x + $maxDistance)) >> 4;
$minZ = ((int) floor($pos->z - $maxDistance)) >> 4;
$maxZ = ((int) floor($pos->z + $maxDistance)) >> 4;
$minX = ((int) floor($pos->x - $maxDistance)) >> Chunk::COORD_BIT_SIZE;
$maxX = ((int) floor($pos->x + $maxDistance)) >> Chunk::COORD_BIT_SIZE;
$minZ = ((int) floor($pos->z - $maxDistance)) >> Chunk::COORD_BIT_SIZE;
$maxZ = ((int) floor($pos->z + $maxDistance)) >> Chunk::COORD_BIT_SIZE;
$currentTargetDistSq = $maxDistance ** 2;
@ -2024,12 +2025,12 @@ class World implements ChunkManager{
* Returns the tile at the specified x,y,z coordinates, or null if it does not exist.
*/
public function getTileAt(int $x, int $y, int $z) : ?Tile{
return ($chunk = $this->loadChunk($x >> 4, $z >> 4)) !== null ? $chunk->getTile($x & 0x0f, $y, $z & 0x0f) : null;
return ($chunk = $this->loadChunk($x >> Chunk::COORD_BIT_SIZE, $z >> Chunk::COORD_BIT_SIZE)) !== null ? $chunk->getTile($x & Chunk::COORD_MASK, $y, $z & Chunk::COORD_MASK) : null;
}
public function getBiomeId(int $x, int $z) : int{
if(($chunk = $this->loadChunk($x >> 4, $z >> 4)) !== null){
return $chunk->getBiomeId($x & 0x0f, $z & 0x0f);
if(($chunk = $this->loadChunk($x >> Chunk::COORD_BIT_SIZE, $z >> Chunk::COORD_BIT_SIZE)) !== null){
return $chunk->getBiomeId($x & Chunk::COORD_MASK, $z & Chunk::COORD_MASK);
}
return BiomeIds::OCEAN; //TODO: this should probably throw instead (terrain not generated yet)
}
@ -2039,14 +2040,14 @@ class World implements ChunkManager{
}
public function setBiomeId(int $x, int $z, int $biomeId) : void{
$chunkX = $x >> 4;
$chunkZ = $z >> 4;
$chunkX = $x >> Chunk::COORD_BIT_SIZE;
$chunkZ = $z >> Chunk::COORD_BIT_SIZE;
if($this->isChunkLocked($chunkX, $chunkZ)){
//the changes would be overwritten when the generation finishes
throw new WorldException("Chunk is currently locked for async generation/population");
}
if(($chunk = $this->loadChunk($chunkX, $chunkZ)) !== null){
$chunk->setBiomeId($x & 0x0f, $z & 0x0f, $biomeId);
$chunk->setBiomeId($x & Chunk::COORD_MASK, $z & Chunk::COORD_MASK, $biomeId);
}else{
//if we allowed this, the modifications would be lost when the chunk is created
throw new WorldException("Cannot set biome in a non-generated chunk");
@ -2075,7 +2076,7 @@ class World implements ChunkManager{
* Returns the chunk containing the given Vector3 position.
*/
public function getOrLoadChunkAtPosition(Vector3 $pos) : ?Chunk{
return $this->loadChunk($pos->getFloorX() >> 4, $pos->getFloorZ() >> 4);
return $this->loadChunk($pos->getFloorX() >> Chunk::COORD_BIT_SIZE, $pos->getFloorZ() >> Chunk::COORD_BIT_SIZE);
}
/**
@ -2228,8 +2229,8 @@ class World implements ChunkManager{
* @throws WorldException if the terrain is not generated
*/
public function getHighestBlockAt(int $x, int $z) : ?int{
if(($chunk = $this->loadChunk($x >> 4, $z >> 4)) !== null){
return $chunk->getHighestBlockAt($x & 0x0f, $z & 0x0f);
if(($chunk = $this->loadChunk($x >> Chunk::COORD_BIT_SIZE, $z >> Chunk::COORD_BIT_SIZE)) !== null){
return $chunk->getHighestBlockAt($x & Chunk::COORD_MASK, $z & Chunk::COORD_MASK);
}
throw new WorldException("Cannot get highest block in an ungenerated chunk");
}
@ -2238,7 +2239,7 @@ class World implements ChunkManager{
* Returns whether the given position is in a loaded area of terrain.
*/
public function isInLoadedTerrain(Vector3 $pos) : bool{
return $this->isChunkLoaded($pos->getFloorX() >> 4, $pos->getFloorZ() >> 4);
return $this->isChunkLoaded($pos->getFloorX() >> Chunk::COORD_BIT_SIZE, $pos->getFloorZ() >> Chunk::COORD_BIT_SIZE);
}
public function isChunkLoaded(int $x, int $z) : bool{
@ -2292,7 +2293,7 @@ class World implements ChunkManager{
if($chunk === null){
throw new \InvalidArgumentException("Cannot add an Entity in an ungenerated chunk");
}
$this->entitiesByChunk[World::chunkHash($pos->getFloorX() >> 4, $pos->getFloorZ() >> 4)][$entity->getId()] = $entity;
$this->entitiesByChunk[World::chunkHash($pos->getFloorX() >> Chunk::COORD_BIT_SIZE, $pos->getFloorZ() >> Chunk::COORD_BIT_SIZE)][$entity->getId()] = $entity;
$this->entityLastKnownPositions[$entity->getId()] = $pos;
if($entity instanceof Player){
@ -2314,7 +2315,7 @@ class World implements ChunkManager{
throw new \InvalidArgumentException("Entity is not tracked by this world (possibly already removed?)");
}
$pos = $this->entityLastKnownPositions[$entity->getId()];
$chunkHash = World::chunkHash($pos->getFloorX() >> 4, $pos->getFloorZ() >> 4);
$chunkHash = World::chunkHash($pos->getFloorX() >> Chunk::COORD_BIT_SIZE, $pos->getFloorZ() >> Chunk::COORD_BIT_SIZE);
if(isset($this->entitiesByChunk[$chunkHash][$entity->getId()])){
unset($this->entitiesByChunk[$chunkHash][$entity->getId()]);
if(count($this->entitiesByChunk[$chunkHash]) === 0){
@ -2343,10 +2344,10 @@ class World implements ChunkManager{
$oldPosition = $this->entityLastKnownPositions[$entity->getId()];
$newPosition = $entity->getPosition();
$oldChunkX = $oldPosition->getFloorX() >> 4;
$oldChunkZ = $oldPosition->getFloorZ() >> 4;
$newChunkX = $newPosition->getFloorX() >> 4;
$newChunkZ = $newPosition->getFloorZ() >> 4;
$oldChunkX = $oldPosition->getFloorX() >> Chunk::COORD_BIT_SIZE;
$oldChunkZ = $oldPosition->getFloorZ() >> Chunk::COORD_BIT_SIZE;
$newChunkX = $newPosition->getFloorX() >> Chunk::COORD_BIT_SIZE;
$newChunkZ = $newPosition->getFloorZ() >> Chunk::COORD_BIT_SIZE;
if($oldChunkX !== $newChunkX || $oldChunkZ !== $newChunkZ){
$oldChunkHash = World::chunkHash($oldChunkX, $oldChunkZ);
@ -2388,8 +2389,8 @@ class World implements ChunkManager{
throw new \InvalidArgumentException("Invalid Tile world");
}
$chunkX = $pos->getFloorX() >> 4;
$chunkZ = $pos->getFloorZ() >> 4;
$chunkX = $pos->getFloorX() >> Chunk::COORD_BIT_SIZE;
$chunkZ = $pos->getFloorZ() >> Chunk::COORD_BIT_SIZE;
if(isset($this->chunks[$hash = World::chunkHash($chunkX, $chunkZ)])){
$this->chunks[$hash]->addTile($tile);
@ -2411,8 +2412,8 @@ class World implements ChunkManager{
throw new \InvalidArgumentException("Invalid Tile world");
}
$chunkX = $pos->getFloorX() >> 4;
$chunkZ = $pos->getFloorZ() >> 4;
$chunkX = $pos->getFloorX() >> Chunk::COORD_BIT_SIZE;
$chunkZ = $pos->getFloorZ() >> Chunk::COORD_BIT_SIZE;
if(isset($this->chunks[$hash = World::chunkHash($chunkX, $chunkZ)])){
$this->chunks[$hash]->removeTile($tile);
@ -2522,7 +2523,7 @@ class World implements ChunkManager{
}
if($tile === null){
$this->getLogger()->warning("Chunk $chunkX $chunkZ: Deleted unknown tile entity type " . $nbt->getString("id", "<unknown>"));
}elseif(!$this->isChunkLoaded($tile->getPosition()->getFloorX() >> 4, $tile->getPosition()->getFloorZ() >> 4)){
}elseif(!$this->isChunkLoaded($tile->getPosition()->getFloorX() >> Chunk::COORD_BIT_SIZE, $tile->getPosition()->getFloorZ() >> Chunk::COORD_BIT_SIZE)){
$this->logger->error("Chunk $chunkX $chunkZ: Found tile saved on wrong chunk - unable to fix due to correct chunk not loaded");
}else{
$this->addTile($tile);
@ -2621,8 +2622,8 @@ class World implements ChunkManager{
*/
public function isSpawnChunk(int $X, int $Z) : bool{
$spawn = $this->getSpawnLocation();
$spawnX = $spawn->x >> 4;
$spawnZ = $spawn->z >> 4;
$spawnX = $spawn->x >> Chunk::COORD_BIT_SIZE;
$spawnZ = $spawn->z >> Chunk::COORD_BIT_SIZE;
return abs($X - $spawnX) <= 1 and abs($Z - $spawnZ) <= 1;
}

View File

@ -31,6 +31,7 @@ use pocketmine\lang\KnownTranslationFactory;
use pocketmine\player\ChunkSelector;
use pocketmine\Server;
use pocketmine\timings\Timings;
use pocketmine\world\format\Chunk;
use pocketmine\world\format\io\exception\CorruptedWorldException;
use pocketmine\world\format\io\exception\UnsupportedWorldFormatException;
use pocketmine\world\format\io\FormatConverter;
@ -268,8 +269,8 @@ class WorldManager{
$this->server->getLogger()->notice($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_backgroundGeneration($name)));
$spawnLocation = $world->getSpawnLocation();
$centerX = $spawnLocation->getFloorX() >> 4;
$centerZ = $spawnLocation->getFloorZ() >> 4;
$centerX = $spawnLocation->getFloorX() >> Chunk::COORD_BIT_SIZE;
$centerZ = $spawnLocation->getFloorZ() >> Chunk::COORD_BIT_SIZE;
$selected = iterator_to_array((new ChunkSelector())->selectChunks(8, $centerX, $centerZ));
$done = 0;

View File

@ -39,6 +39,10 @@ class Chunk{
public const MAX_SUBCHUNKS = 16;
public const EDGE_LENGTH = SubChunk::EDGE_LENGTH;
public const COORD_BIT_SIZE = SubChunk::COORD_BIT_SIZE;
public const COORD_MASK = SubChunk::COORD_MASK;
/** @var int */
private $terrainDirtyFlags = 0;
@ -72,7 +76,7 @@ class Chunk{
$this->subChunks[$y] = $subChunks[$y] ?? new SubChunk(BlockLegacyIds::AIR << Block::INTERNAL_METADATA_BITS, []);
}
$val = ($this->subChunks->getSize() * 16);
$val = ($this->subChunks->getSize() * SubChunk::EDGE_LENGTH);
$this->heightMap = $heightMap ?? new HeightArray(array_fill(0, 256, $val));
$this->biomeIds = $biomeIds ?? BiomeArray::fill(BiomeIds::OCEAN);
}
@ -94,14 +98,14 @@ class Chunk{
* @return int bitmap, (id << 4) | meta
*/
public function getFullBlock(int $x, int $y, int $z) : int{
return $this->getSubChunk($y >> 4)->getFullBlock($x, $y & 0x0f, $z);
return $this->getSubChunk($y >> SubChunk::COORD_BIT_SIZE)->getFullBlock($x, $y & SubChunk::COORD_MASK, $z);
}
/**
* Sets the blockstate at the given coordinate by internal ID.
*/
public function setFullBlock(int $x, int $y, int $z, int $block) : void{
$this->getSubChunk($y >> 4)->setFullBlock($x, $y & 0xf, $z, $block);
$this->getSubChunk($y >> SubChunk::COORD_BIT_SIZE)->setFullBlock($x, $y & SubChunk::COORD_MASK, $z, $block);
$this->terrainDirtyFlags |= self::DIRTY_FLAG_TERRAIN;
}
@ -117,7 +121,7 @@ class Chunk{
for($y = $this->subChunks->count() - 1; $y >= 0; --$y){
$height = $this->getSubChunk($y)->getHighestBlockAt($x, $z);
if($height !== null){
return $height | ($y << 4);
return $height | ($y << SubChunk::COORD_BIT_SIZE);
}
}
@ -328,6 +332,8 @@ class Chunk{
* @param int $z 0-15
*/
public static function blockHash(int $x, int $y, int $z) : int{
return ($y << 8) | (($z & 0x0f) << 4) | ($x & 0x0f);
return ($y << (2 * SubChunk::COORD_BIT_SIZE)) |
(($z & SubChunk::COORD_MASK) << SubChunk::COORD_BIT_SIZE) |
($x & SubChunk::COORD_MASK);
}
}

View File

@ -28,6 +28,10 @@ use function array_values;
use function count;
class SubChunk{
public const COORD_BIT_SIZE = 4;
public const COORD_MASK = ~(~0 << self::COORD_BIT_SIZE);
public const EDGE_LENGTH = 1 << self::COORD_BIT_SIZE;
/** @var int */
private $emptyBlockId;
/** @var PalettedBlockArray[] */
@ -100,7 +104,7 @@ class SubChunk{
if(count($this->blockLayers) === 0){
return null;
}
for($y = 15; $y >= 0; --$y){
for($y = self::EDGE_LENGTH - 1; $y >= 0; --$y){
if($this->blockLayers[0]->get($x, $y, $z) !== $this->emptyBlockId){
return $y;
}

View File

@ -216,8 +216,8 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
self::deserializeExtraDataKey($chunkVersion, $key, $x, $fullY, $z);
$ySub = ($fullY >> 4) & 0xf;
$y = $key & 0xf;
$ySub = ($fullY >> SubChunk::COORD_BIT_SIZE);
$y = $key & SubChunk::COORD_MASK;
$blockId = $value & 0xff;
$blockData = ($value >> 8) & 0xf;

View File

@ -28,6 +28,7 @@ use pocketmine\item\LegacyStringToItemParser;
use pocketmine\item\LegacyStringToItemParserException;
use pocketmine\world\ChunkManager;
use pocketmine\world\format\Chunk;
use pocketmine\world\format\SubChunk;
use pocketmine\world\generator\object\OreType;
use pocketmine\world\generator\populator\Ore;
use pocketmine\world\generator\populator\Populator;
@ -141,20 +142,20 @@ class Flat extends Generator{
protected function generateBaseChunk() : void{
$this->chunk = new Chunk();
for($Z = 0; $Z < 16; ++$Z){
for($X = 0; $X < 16; ++$X){
for($Z = 0; $Z < Chunk::EDGE_LENGTH; ++$Z){
for($X = 0; $X < Chunk::EDGE_LENGTH; ++$X){
$this->chunk->setBiomeId($X, $Z, $this->biome);
}
}
$count = count($this->structure);
for($sy = 0; $sy < $count; $sy += 16){
$subchunk = $this->chunk->getSubChunk($sy >> 4);
for($y = 0; $y < 16 and isset($this->structure[$y | $sy]); ++$y){
for($sy = 0; $sy < $count; $sy += SubChunk::EDGE_LENGTH){
$subchunk = $this->chunk->getSubChunk($sy >> SubChunk::COORD_BIT_SIZE);
for($y = 0; $y < SubChunk::EDGE_LENGTH and isset($this->structure[$y | $sy]); ++$y){
$id = $this->structure[$y | $sy];
for($Z = 0; $Z < 16; ++$Z){
for($X = 0; $X < 16; ++$X){
for($Z = 0; $Z < SubChunk::EDGE_LENGTH; ++$Z){
for($X = 0; $X < SubChunk::EDGE_LENGTH; ++$X){
$subchunk->setFullBlock($X, $y, $Z, $id);
}
}

View File

@ -27,6 +27,7 @@ use pocketmine\block\VanillaBlocks;
use pocketmine\data\bedrock\BiomeIds;
use pocketmine\world\biome\BiomeRegistry;
use pocketmine\world\ChunkManager;
use pocketmine\world\format\Chunk;
use pocketmine\world\generator\Generator;
use pocketmine\world\generator\InvalidGeneratorOptionsException;
use pocketmine\world\generator\noise\Simplex;
@ -72,7 +73,7 @@ class Nether extends Generator{
public function generateChunk(ChunkManager $world, int $chunkX, int $chunkZ) : void{
$this->random->setSeed(0xdeadbeef ^ ($chunkX << 8) ^ $chunkZ ^ $this->seed);
$noise = $this->noiseBase->getFastNoise3D(16, 128, 16, 4, 8, 4, $chunkX * 16, 0, $chunkZ * 16);
$noise = $this->noiseBase->getFastNoise3D(Chunk::EDGE_LENGTH, 128, Chunk::EDGE_LENGTH, 4, 8, 4, $chunkX * Chunk::EDGE_LENGTH, 0, $chunkZ * Chunk::EDGE_LENGTH);
$chunk = $world->getChunk($chunkX, $chunkZ);
@ -80,8 +81,8 @@ class Nether extends Generator{
$netherrack = VanillaBlocks::NETHERRACK()->getFullId();
$stillLava = VanillaBlocks::LAVA()->getFullId();
for($x = 0; $x < 16; ++$x){
for($z = 0; $z < 16; ++$z){
for($x = 0; $x < Chunk::EDGE_LENGTH; ++$x){
for($z = 0; $z < Chunk::EDGE_LENGTH; ++$z){
$chunk->setBiomeId($x, $z, BiomeIds::HELL);
for($y = 0; $y < 128; ++$y){

View File

@ -28,6 +28,7 @@ use pocketmine\data\bedrock\BiomeIds;
use pocketmine\world\biome\Biome;
use pocketmine\world\biome\BiomeRegistry;
use pocketmine\world\ChunkManager;
use pocketmine\world\format\Chunk;
use pocketmine\world\generator\biome\BiomeSelector;
use pocketmine\world\generator\Gaussian;
use pocketmine\world\generator\Generator;
@ -144,7 +145,7 @@ class Normal extends Generator{
public function generateChunk(ChunkManager $world, int $chunkX, int $chunkZ) : void{
$this->random->setSeed(0xdeadbeef ^ ($chunkX << 8) ^ $chunkZ ^ $this->seed);
$noise = $this->noiseBase->getFastNoise3D(16, 128, 16, 4, 8, 4, $chunkX * 16, 0, $chunkZ * 16);
$noise = $this->noiseBase->getFastNoise3D(Chunk::EDGE_LENGTH, 128, Chunk::EDGE_LENGTH, 4, 8, 4, $chunkX * Chunk::EDGE_LENGTH, 0, $chunkZ * Chunk::EDGE_LENGTH);
$chunk = $world->getChunk($chunkX, $chunkZ);
@ -154,11 +155,11 @@ class Normal extends Generator{
$stillWater = VanillaBlocks::WATER()->getFullId();
$stone = VanillaBlocks::STONE()->getFullId();
$baseX = $chunkX * 16;
$baseZ = $chunkZ * 16;
for($x = 0; $x < 16; ++$x){
$baseX = $chunkX * Chunk::EDGE_LENGTH;
$baseZ = $chunkZ * Chunk::EDGE_LENGTH;
for($x = 0; $x < Chunk::EDGE_LENGTH; ++$x){
$absoluteX = $baseX + $x;
for($z = 0; $z < 16; ++$z){
for($z = 0; $z < Chunk::EDGE_LENGTH; ++$z){
$absoluteZ = $baseZ + $z;
$minSum = 0;
$maxSum = 0;

View File

@ -29,6 +29,7 @@ use pocketmine\block\Liquid;
use pocketmine\utils\Random;
use pocketmine\world\biome\BiomeRegistry;
use pocketmine\world\ChunkManager;
use pocketmine\world\format\Chunk;
use function count;
use function min;
@ -38,8 +39,8 @@ class GroundCover implements Populator{
$chunk = $world->getChunk($chunkX, $chunkZ);
$factory = BlockFactory::getInstance();
$biomeRegistry = BiomeRegistry::getInstance();
for($x = 0; $x < 16; ++$x){
for($z = 0; $z < 16; ++$z){
for($x = 0; $x < Chunk::EDGE_LENGTH; ++$x){
for($z = 0; $z < Chunk::EDGE_LENGTH; ++$z){
$biome = $biomeRegistry->getBiome($chunk->getBiomeId($x, $z));
$cover = $biome->getGroundCover();
if(count($cover) > 0){

View File

@ -25,6 +25,7 @@ namespace pocketmine\world\generator\populator;
use pocketmine\utils\Random;
use pocketmine\world\ChunkManager;
use pocketmine\world\format\Chunk;
use pocketmine\world\generator\object\Ore as ObjectOre;
use pocketmine\world\generator\object\OreType;
@ -36,9 +37,9 @@ class Ore implements Populator{
foreach($this->oreTypes as $type){
$ore = new ObjectOre($random, $type);
for($i = 0; $i < $ore->type->clusterCount; ++$i){
$x = $random->nextRange($chunkX << 4, ($chunkX << 4) + 15);
$x = $random->nextRange($chunkX << Chunk::COORD_BIT_SIZE, ($chunkX << Chunk::COORD_BIT_SIZE) + Chunk::EDGE_LENGTH - 1);
$y = $random->nextRange($ore->type->minHeight, $ore->type->maxHeight);
$z = $random->nextRange($chunkZ << 4, ($chunkZ << 4) + 15);
$z = $random->nextRange($chunkZ << Chunk::COORD_BIT_SIZE, ($chunkZ << Chunk::COORD_BIT_SIZE) + Chunk::EDGE_LENGTH - 1);
if($ore->canPlaceObject($world, $x, $y, $z)){
$ore->placeObject($world, $x, $y, $z);
}

View File

@ -27,6 +27,7 @@ use pocketmine\block\BlockLegacyIds;
use pocketmine\block\VanillaBlocks;
use pocketmine\utils\Random;
use pocketmine\world\ChunkManager;
use pocketmine\world\format\Chunk;
class TallGrass implements Populator{
/** @var int */
@ -47,8 +48,8 @@ class TallGrass implements Populator{
$block = VanillaBlocks::TALL_GRASS();
for($i = 0; $i < $amount; ++$i){
$x = $random->nextRange($chunkX * 16, $chunkX * 16 + 15);
$z = $random->nextRange($chunkZ * 16, $chunkZ * 16 + 15);
$x = $random->nextRange($chunkX * Chunk::EDGE_LENGTH, $chunkX * Chunk::EDGE_LENGTH + (Chunk::EDGE_LENGTH - 1));
$z = $random->nextRange($chunkZ * Chunk::EDGE_LENGTH, $chunkZ * Chunk::EDGE_LENGTH + (Chunk::EDGE_LENGTH - 1));
$y = $this->getHighestWorkableBlock($world, $x, $z);
if($y !== -1 and $this->canTallGrassStay($world, $x, $y, $z)){

View File

@ -27,6 +27,7 @@ use pocketmine\block\BlockLegacyIds;
use pocketmine\block\utils\TreeType;
use pocketmine\utils\Random;
use pocketmine\world\ChunkManager;
use pocketmine\world\format\Chunk;
use pocketmine\world\generator\object\TreeFactory;
class Tree implements Populator{
@ -56,8 +57,8 @@ class Tree implements Populator{
public function populate(ChunkManager $world, int $chunkX, int $chunkZ, Random $random) : void{
$amount = $random->nextRange(0, $this->randomAmount) + $this->baseAmount;
for($i = 0; $i < $amount; ++$i){
$x = $random->nextRange($chunkX << 4, ($chunkX << 4) + 15);
$z = $random->nextRange($chunkZ << 4, ($chunkZ << 4) + 15);
$x = $random->nextRange($chunkX << Chunk::COORD_BIT_SIZE, ($chunkX << Chunk::COORD_BIT_SIZE) + Chunk::EDGE_LENGTH);
$z = $random->nextRange($chunkZ << Chunk::COORD_BIT_SIZE, ($chunkZ << Chunk::COORD_BIT_SIZE) + Chunk::EDGE_LENGTH);
$y = $this->getHighestWorkableBlock($world, $x, $z);
if($y === -1){
continue;

View File

@ -54,7 +54,7 @@ class BlockLightUpdate extends LightUpdate{
public function recalculateNode(int $x, int $y, int $z) : void{
if($this->subChunkExplorer->moveTo($x, $y, $z) !== SubChunkExplorerStatus::INVALID){
$block = $this->subChunkExplorer->currentSubChunk->getFullBlock($x & 0xf, $y & 0xf, $z & 0xf);
$block = $this->subChunkExplorer->currentSubChunk->getFullBlock($x & SubChunk::COORD_MASK, $y & SubChunk::COORD_MASK, $z & SubChunk::COORD_MASK);
$this->setAndUpdateLight($x, $y, $z, max($this->lightEmitters[$block], $this->getHighestAdjacentLight($x, $y, $z) - $this->lightFilters[$block]));
}
}
@ -72,7 +72,7 @@ class BlockLightUpdate extends LightUpdate{
foreach($subChunk->getBlockLayers() as $layer){
foreach($layer->getPalette() as $state){
if($this->lightEmitters[$state] > 0){
$lightSources += $this->scanForLightEmittingBlocks($subChunk, $chunkX << 4, $subChunkY << 4, $chunkZ << 4);
$lightSources += $this->scanForLightEmittingBlocks($subChunk, $chunkX << SubChunk::COORD_BIT_SIZE, $subChunkY << SubChunk::COORD_BIT_SIZE, $chunkZ << SubChunk::COORD_BIT_SIZE);
break 2;
}
}
@ -84,9 +84,9 @@ class BlockLightUpdate extends LightUpdate{
private function scanForLightEmittingBlocks(SubChunk $subChunk, int $baseX, int $baseY, int $baseZ) : int{
$lightSources = 0;
for($x = 0; $x < 16; ++$x){
for($z = 0; $z < 16; ++$z){
for($y = 0; $y < 16; ++$y){
for($x = 0; $x < SubChunk::EDGE_LENGTH; ++$x){
for($z = 0; $z < SubChunk::EDGE_LENGTH; ++$z){
for($y = 0; $y < SubChunk::EDGE_LENGTH; ++$y){
$light = $this->lightEmitters[$subChunk->getFullBlock($x, $y, $z)];
if($light > 0){
$this->setAndUpdateLight(

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\world\light;
use pocketmine\world\format\LightArray;
use pocketmine\world\format\SubChunk;
use pocketmine\world\utils\SubChunkExplorer;
use pocketmine\world\utils\SubChunkExplorerStatus;
use pocketmine\world\World;
@ -77,7 +78,7 @@ abstract class LightUpdate{
protected function getEffectiveLight(int $x, int $y, int $z) : int{
if($this->subChunkExplorer->moveTo($x, $y, $z) !== SubChunkExplorerStatus::INVALID){
return $this->getCurrentLightArray()->get($x & 0xf, $y & 0xf, $z & 0xf);
return $this->getCurrentLightArray()->get($x & SubChunk::COORD_MASK, $y & SubChunk::COORD_MASK, $z & SubChunk::COORD_MASK);
}
return 0;
}
@ -101,10 +102,10 @@ abstract class LightUpdate{
foreach($this->updateNodes as $blockHash => [$x, $y, $z, $newLevel]){
if($this->subChunkExplorer->moveTo($x, $y, $z) !== SubChunkExplorerStatus::INVALID){
$lightArray = $this->getCurrentLightArray();
$oldLevel = $lightArray->get($x & 0xf, $y & 0xf, $z & 0xf);
$oldLevel = $lightArray->get($x & SubChunk::COORD_MASK, $y & SubChunk::COORD_MASK, $z & SubChunk::COORD_MASK);
if($oldLevel !== $newLevel){
$lightArray->set($x & 0xf, $y & 0xf, $z & 0xf, $newLevel);
$lightArray->set($x & SubChunk::COORD_MASK, $y & SubChunk::COORD_MASK, $z & SubChunk::COORD_MASK, $newLevel);
if($oldLevel < $newLevel){ //light increased
$context->spreadVisited[$blockHash] = true;
$context->spreadQueue->enqueue([$x, $y, $z]);
@ -167,9 +168,9 @@ abstract class LightUpdate{
protected function computeRemoveLight(int $x, int $y, int $z, int $oldAdjacentLevel, LightPropagationContext $context) : void{
$lightArray = $this->getCurrentLightArray();
$lx = $x & 0xf;
$ly = $y & 0xf;
$lz = $z & 0xf;
$lx = $x & SubChunk::COORD_MASK;
$ly = $y & SubChunk::COORD_MASK;
$lz = $z & SubChunk::COORD_MASK;
$current = $lightArray->get($lx, $ly, $lz);
if($current !== 0 and $current < $oldAdjacentLevel){
@ -191,9 +192,9 @@ abstract class LightUpdate{
protected function computeSpreadLight(int $x, int $y, int $z, int $newAdjacentLevel, LightPropagationContext $context) : void{
$lightArray = $this->getCurrentLightArray();
$lx = $x & 0xf;
$ly = $y & 0xf;
$lz = $z & 0xf;
$lx = $x & SubChunk::COORD_MASK;
$ly = $y & SubChunk::COORD_MASK;
$lz = $z & SubChunk::COORD_MASK;
$current = $lightArray->get($lx, $ly, $lz);
$potentialLight = $newAdjacentLevel - $this->lightFilters[$this->subChunkExplorer->currentSubChunk->getFullBlock($lx, $ly, $lz)];

View File

@ -26,6 +26,7 @@ namespace pocketmine\world\light;
use pocketmine\world\format\Chunk;
use pocketmine\world\format\HeightArray;
use pocketmine\world\format\LightArray;
use pocketmine\world\format\SubChunk;
use pocketmine\world\utils\SubChunkExplorer;
use pocketmine\world\utils\SubChunkExplorerStatus;
use pocketmine\world\World;
@ -68,17 +69,17 @@ class SkyLightUpdate extends LightUpdate{
}
$chunk = $this->subChunkExplorer->currentChunk;
$oldHeightMap = $chunk->getHeightMap($x & 0xf, $z & 0xf);
$source = $this->subChunkExplorer->currentSubChunk->getFullBlock($x & 0xf, $y & 0xf, $z & 0xf);
$oldHeightMap = $chunk->getHeightMap($x & Chunk::COORD_MASK, $z & Chunk::COORD_MASK);
$source = $this->subChunkExplorer->currentSubChunk->getFullBlock($x & SubChunk::COORD_MASK, $y & SubChunk::COORD_MASK, $z & SubChunk::COORD_MASK);
$yPlusOne = $y + 1;
if($yPlusOne === $oldHeightMap){ //Block changed directly beneath the heightmap. Check if a block was removed or changed to a different light-filter.
$newHeightMap = self::recalculateHeightMapColumn($chunk, $x & 0x0f, $z & 0x0f, $this->directSkyLightBlockers);
$chunk->setHeightMap($x & 0xf, $z & 0xf, $newHeightMap);
$newHeightMap = self::recalculateHeightMapColumn($chunk, $x & Chunk::COORD_MASK, $z & Chunk::COORD_MASK, $this->directSkyLightBlockers);
$chunk->setHeightMap($x & Chunk::COORD_MASK, $z & Chunk::COORD_MASK, $newHeightMap);
}elseif($yPlusOne > $oldHeightMap){ //Block changed above the heightmap.
if($this->directSkyLightBlockers[$source]){
$chunk->setHeightMap($x & 0xf, $z & 0xf, $yPlusOne);
$chunk->setHeightMap($x & Chunk::COORD_MASK, $z & Chunk::COORD_MASK, $yPlusOne);
$newHeightMap = $yPlusOne;
}else{ //Block changed which has no effect on direct sky light, for example placing or removing glass.
return;
@ -112,7 +113,7 @@ class SkyLightUpdate extends LightUpdate{
//setAndUpdateLight() won't bother propagating from nodes that are already what we want to change them to, so we
//have to avoid filling full light for any subchunk that contains a heightmap Y coordinate
$highestHeightMapPlusOne = max($chunk->getHeightMapArray()) + 1;
$lowestClearSubChunk = ($highestHeightMapPlusOne >> 4) + (($highestHeightMapPlusOne & 0xf) !== 0 ? 1 : 0);
$lowestClearSubChunk = ($highestHeightMapPlusOne >> SubChunk::COORD_BIT_SIZE) + (($highestHeightMapPlusOne & SubChunk::COORD_MASK) !== 0 ? 1 : 0);
$chunkHeight = $chunk->getSubChunks()->count();
for($y = 0; $y < $lowestClearSubChunk && $y < $chunkHeight; $y++){
$chunk->getSubChunk($y)->setBlockSkyLightArray(LightArray::fill(0));
@ -123,10 +124,10 @@ class SkyLightUpdate extends LightUpdate{
$lightSources = 0;
$baseX = $chunkX << 4;
$baseZ = $chunkZ << 4;
for($x = 0; $x < 16; ++$x){
for($z = 0; $z < 16; ++$z){
$baseX = $chunkX << Chunk::COORD_BIT_SIZE;
$baseZ = $chunkZ << Chunk::COORD_BIT_SIZE;
for($x = 0; $x < Chunk::EDGE_LENGTH; ++$x){
for($z = 0; $z < Chunk::EDGE_LENGTH; ++$z){
$currentHeight = $chunk->getHeightMap($x, $z);
$maxAdjacentHeight = 0;
if($x !== 0){
@ -154,9 +155,9 @@ class SkyLightUpdate extends LightUpdate{
$this->setAndUpdateLight($x + $baseX, $y, $z + $baseZ, 15);
$lightSources++;
}
for($y = $nodeColumnEnd + 1, $yMax = $lowestClearSubChunk * 16; $y < $yMax; $y++){
for($y = $nodeColumnEnd + 1, $yMax = $lowestClearSubChunk * SubChunk::EDGE_LENGTH; $y < $yMax; $y++){
if($this->subChunkExplorer->moveTo($x + $baseX, $y, $z + $baseZ) !== SubChunkExplorerStatus::INVALID){
$this->getCurrentLightArray()->set($x, $y & 0xf, $z, 15);
$this->getCurrentLightArray()->set($x, $y & SubChunk::COORD_MASK, $z, 15);
}
}
}
@ -183,13 +184,13 @@ class SkyLightUpdate extends LightUpdate{
return $result;
}
for($z = 0; $z < 16; ++$z){
for($x = 0; $x < 16; ++$x){
for($z = 0; $z < Chunk::EDGE_LENGTH; ++$z){
for($x = 0; $x < Chunk::EDGE_LENGTH; ++$x){
$y = null;
for($subChunkY = $maxSubChunkY; $subChunkY >= 0; $subChunkY--){
$subHighestBlockY = $chunk->getSubChunk($subChunkY)->getHighestBlockAt($x, $z);
if($subHighestBlockY !== null){
$y = ($subChunkY * 16) + $subHighestBlockY;
$y = ($subChunkY * SubChunk::EDGE_LENGTH) + $subHighestBlockY;
break;
}
}

View File

@ -51,8 +51,8 @@ class SubChunkExplorer{
* @phpstan-return SubChunkExplorerStatus::*
*/
public function moveTo(int $x, int $y, int $z) : int{
$newChunkX = $x >> 4;
$newChunkZ = $z >> 4;
$newChunkX = $x >> SubChunk::COORD_BIT_SIZE;
$newChunkZ = $z >> SubChunk::COORD_BIT_SIZE;
if($this->currentChunk === null or $this->currentX !== $newChunkX or $this->currentZ !== $newChunkZ){
$this->currentX = $newChunkX;
$this->currentZ = $newChunkZ;
@ -64,7 +64,7 @@ class SubChunkExplorer{
}
}
$newChunkY = $y >> 4;
$newChunkY = $y >> SubChunk::COORD_BIT_SIZE;
if($this->currentSubChunk === null or $this->currentY !== $newChunkY){
$this->currentY = $newChunkY;
@ -85,7 +85,7 @@ class SubChunkExplorer{
*/
public function moveToChunk(int $chunkX, int $chunkY, int $chunkZ) : int{
//this is a cold path, so we don't care much if it's a bit slower (extra fcall overhead)
return $this->moveTo($chunkX << 4, $chunkY << 4, $chunkZ << 4);
return $this->moveTo($chunkX << SubChunk::COORD_BIT_SIZE, $chunkY << SubChunk::COORD_BIT_SIZE, $chunkZ << SubChunk::COORD_BIT_SIZE);
}
/**