mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-21 08:17:34 +00:00
Stop hardcoding chunk dimensions everywhere (#4443)
This commit is contained in:
parent
9d5a86fe53
commit
4111d92b98
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
2
src/network/mcpe/cache/ChunkCache.php
vendored
2
src/network/mcpe/cache/ChunkCache.php
vendored
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
@ -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){
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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){
|
||||
|
@ -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;
|
||||
|
@ -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){
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)){
|
||||
|
@ -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;
|
||||
|
@ -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(
|
||||
|
@ -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)];
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user