Removed masks for Y coordinates, fixed bugs related to out-of-bounds coordinates, fixed #914 (#915)

This commit is contained in:
Dylan K. Taylor 2017-06-21 10:55:38 +01:00 committed by GitHub
parent 69ae37d191
commit 0f79b19fdc
6 changed files with 105 additions and 36 deletions

View File

@ -131,4 +131,22 @@ interface ChunkManager{
* @return int
*/
public function getSeed();
/**
* Returns the height of the world
* @return int
*/
public function getWorldHeight() : int;
/**
* Returns whether the specified coordinates are within the valid world boundaries, taking world format limitations
* into account.
*
* @param float $x
* @param float $y
* @param float $z
*
* @return bool
*/
public function isInWorld(float $x, float $y, float $z) : bool;
}

View File

@ -89,7 +89,7 @@ class Explosion{
$vBlock->x = $pointerX >= $x ? $x : $x - 1;
$vBlock->y = $pointerY >= $y ? $y : $y - 1;
$vBlock->z = $pointerZ >= $z ? $z : $z - 1;
if($vBlock->y < 0 or $vBlock->y >= Level::Y_MAX){
if(!$this->level->isInWorld($vBlock->x, $vBlock->y, $vBlock->z)){
break;
}
$block = $this->level->getBlock($vBlock);

View File

@ -269,6 +269,9 @@ class Level implements ChunkManager, Metadatable{
}
public static function blockHash(int $x, int $y, int $z){
if($y < 0 or $y >= Level::Y_MAX){
throw new \InvalidArgumentException("Y coordinate $y is out of range!");
}
return (($x & 0xFFFFFFF) << 36) | (($y & Level::Y_MASK) << 28) | ($z & 0xFFFFFFF);
}
@ -1078,7 +1081,10 @@ class Level implements ChunkManager, Metadatable{
* @param int $delay
*/
public function scheduleDelayedBlockUpdate(Vector3 $pos, int $delay){
if(isset($this->scheduledBlockUpdateQueueIndex[$index = Level::blockHash($pos->x, $pos->y, $pos->z)]) and $this->scheduledBlockUpdateQueueIndex[$index] <= $delay){
if(
!$this->isInWorld($pos->x, $pos->y, $pos->z) or
(isset($this->scheduledBlockUpdateQueueIndex[$index = Level::blockHash($pos->x, $pos->y, $pos->z)]) and $this->scheduledBlockUpdateQueueIndex[$index] <= $delay)
){
return;
}
$this->scheduledBlockUpdateQueueIndex[$index] = $delay;
@ -1094,12 +1100,12 @@ class Level implements ChunkManager, Metadatable{
public function scheduleNeighbourBlockUpdates(Vector3 $pos){
$pos = $pos->floor();
$this->neighbourBlockUpdateQueue->enqueue(Level::blockHash($pos->x + 1, $pos->y, $pos->z));
$this->neighbourBlockUpdateQueue->enqueue(Level::blockHash($pos->x - 1, $pos->y, $pos->z));
$this->neighbourBlockUpdateQueue->enqueue(Level::blockHash($pos->x, $pos->y + 1, $pos->z));
$this->neighbourBlockUpdateQueue->enqueue(Level::blockHash($pos->x, $pos->y - 1, $pos->z));
$this->neighbourBlockUpdateQueue->enqueue(Level::blockHash($pos->x, $pos->y, $pos->z + 1));
$this->neighbourBlockUpdateQueue->enqueue(Level::blockHash($pos->x, $pos->y, $pos->z - 1));
for($i = 0; $i <= 5; ++$i){
$side = $pos->getSide($i);
if($this->isInWorld($side->x, $side->y, $side->z)){
$this->neighbourBlockUpdateQueue->enqueue(Level::blockHash($side->x, $side->y, $side->z));
}
}
}
/**
@ -1298,23 +1304,39 @@ class Level implements ChunkManager, Metadatable{
return $this->getChunk($x >> 4, $z >> 4, false)->getFullBlock($x & 0x0f, $y, $z & 0x0f);
}
public function isInWorld(float $x, float $y, float $z) : bool{
return (
$x <= INT32_MAX and $x >= INT32_MIN and
$y < $this->getWorldHeight() and $y >= 0 and
$z <= INT32_MAX and $z >= INT32_MIN
);
}
/**
* Gets the Block object on the Vector3 location
* Gets the Block object at the Vector3 location
*
* Note for plugin developers: If you are using this method a lot (thousands of times for many positions for
* example), you may want to set addToCache to false to avoid using excessive amounts of memory.
*
* @param Vector3 $pos
* @param bool $cached
* @param bool $cached Whether to use the block cache for getting the block (faster, but may be inaccurate)
* @param bool $addToCache Whether to cache the block object created by this method call.
*
* @return Block
*/
public function getBlock(Vector3 $pos, $cached = true) : Block{
public function getBlock(Vector3 $pos, bool $cached = true, bool $addToCache = true) : Block{
$pos = $pos->floor();
$index = Level::blockHash($pos->x, $pos->y, $pos->z);
if($cached and isset($this->blockCache[$index])){
return $this->blockCache[$index];
}elseif($pos->y >= 0 and $pos->y < $this->provider->getWorldHeight() and isset($this->chunks[$chunkIndex = Level::chunkHash($pos->x >> 4, $pos->z >> 4)])){
$fullState = $this->chunks[$chunkIndex]->getFullBlock($pos->x & 0x0f, $pos->y & Level::Y_MASK, $pos->z & 0x0f);
}else{
$fullState = 0;
$fullState = 0;
$index = null;
if($this->isInWorld($pos->x, $pos->y, $pos->z)){
$index = Level::blockHash($pos->x, $pos->y, $pos->z);
if($cached and isset($this->blockCache[$index])){
return $this->blockCache[$index];
}elseif(isset($this->chunks[$chunkIndex = Level::chunkHash($pos->x >> 4, $pos->z >> 4)])){
$fullState = $this->chunks[$chunkIndex]->getFullBlock($pos->x & 0x0f, $pos->y, $pos->z & 0x0f);
}
}
$block = clone $this->blockStates[$fullState & 0xfff];
@ -1324,7 +1346,11 @@ class Level implements ChunkManager, Metadatable{
$block->z = $pos->z;
$block->level = $this;
return $this->blockCache[$index] = $block;
if($addToCache and $index !== null){
$this->blockCache[$index] = $block;
}
return $block;
}
public function updateAllLight(Vector3 $pos){
@ -1446,13 +1472,13 @@ class Level implements ChunkManager, Metadatable{
*/
public function setBlock(Vector3 $pos, Block $block, bool $direct = false, bool $update = true) : bool{
$pos = $pos->floor();
if($pos->y < 0 or $pos->y >= $this->provider->getWorldHeight()){
if(!$this->isInWorld($pos->x, $pos->y, $pos->z)){
return false;
}
$this->timings->setBlock->startTiming();
if($this->getChunk($pos->x >> 4, $pos->z >> 4, true)->setBlock($pos->x & 0x0f, $pos->y & Level::Y_MASK, $pos->z & 0x0f, $block->getId(), $block->getDamage())){
if($this->getChunk($pos->x >> 4, $pos->z >> 4, true)->setBlock($pos->x & 0x0f, $pos->y, $pos->z & 0x0f, $block->getId(), $block->getDamage())){
if(!($pos instanceof Position)){
$pos = $this->temporalPosition->setComponents($pos->x, $pos->y, $pos->z);
}
@ -1942,7 +1968,7 @@ class Level implements ChunkManager, Metadatable{
$chunk = $this->getChunk($pos->x >> 4, $pos->z >> 4, false);
if($chunk !== null){
return $chunk->getTile($pos->x & 0x0f, $pos->y & Level::Y_MASK, $pos->z & 0x0f);
return $chunk->getTile($pos->x & 0x0f, $pos->y, $pos->z & 0x0f);
}
return null;
@ -1982,7 +2008,7 @@ class Level implements ChunkManager, Metadatable{
* @return int 0-255
*/
public function getBlockIdAt(int $x, int $y, int $z) : int{
return $this->getChunk($x >> 4, $z >> 4, true)->getBlockId($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f);
return $this->getChunk($x >> 4, $z >> 4, true)->getBlockId($x & 0x0f, $y, $z & 0x0f);
}
/**
@ -1995,7 +2021,7 @@ class Level implements ChunkManager, Metadatable{
*/
public function setBlockIdAt(int $x, int $y, int $z, int $id){
unset($this->blockCache[Level::blockHash($x, $y, $z)]);
$this->getChunk($x >> 4, $z >> 4, true)->setBlockId($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f, $id & 0xff);
$this->getChunk($x >> 4, $z >> 4, true)->setBlockId($x & 0x0f, $y, $z & 0x0f, $id & 0xff);
if(!isset($this->changedBlocks[$index = Level::chunkHash($x >> 4, $z >> 4)])){
$this->changedBlocks[$index] = [];
@ -2016,7 +2042,7 @@ class Level implements ChunkManager, Metadatable{
* @return int 16-bit
*/
public function getBlockExtraDataAt(int $x, int $y, int $z) : int{
return $this->getChunk($x >> 4, $z >> 4, true)->getBlockExtraData($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f);
return $this->getChunk($x >> 4, $z >> 4, true)->getBlockExtraData($x & 0x0f, $y, $z & 0x0f);
}
/**
@ -2029,7 +2055,7 @@ class Level implements ChunkManager, Metadatable{
* @param int $data
*/
public function setBlockExtraDataAt(int $x, int $y, int $z, int $id, int $data){
$this->getChunk($x >> 4, $z >> 4, true)->setBlockExtraData($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f, ($data << 8) | $id);
$this->getChunk($x >> 4, $z >> 4, true)->setBlockExtraData($x & 0x0f, $y, $z & 0x0f, ($data << 8) | $id);
$this->sendBlockExtraData($x, $y, $z, $id, $data);
}
@ -2044,7 +2070,7 @@ class Level implements ChunkManager, Metadatable{
* @return int 0-15
*/
public function getBlockDataAt(int $x, int $y, int $z) : int{
return $this->getChunk($x >> 4, $z >> 4, true)->getBlockData($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f);
return $this->getChunk($x >> 4, $z >> 4, true)->getBlockData($x & 0x0f, $y, $z & 0x0f);
}
/**
@ -2057,7 +2083,7 @@ class Level implements ChunkManager, Metadatable{
*/
public function setBlockDataAt(int $x, int $y, int $z, int $data){
unset($this->blockCache[Level::blockHash($x, $y, $z)]);
$this->getChunk($x >> 4, $z >> 4, true)->setBlockData($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f, $data & 0x0f);
$this->getChunk($x >> 4, $z >> 4, true)->setBlockData($x & 0x0f, $y, $z & 0x0f, $data & 0x0f);
if(!isset($this->changedBlocks[$index = Level::chunkHash($x >> 4, $z >> 4)])){
$this->changedBlocks[$index] = [];
@ -2078,7 +2104,7 @@ class Level implements ChunkManager, Metadatable{
* @return int 0-15
*/
public function getBlockSkyLightAt(int $x, int $y, int $z) : int{
return $this->getChunk($x >> 4, $z >> 4, true)->getBlockSkyLight($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f);
return $this->getChunk($x >> 4, $z >> 4, true)->getBlockSkyLight($x & 0x0f, $y, $z & 0x0f);
}
/**
@ -2090,7 +2116,7 @@ class Level implements ChunkManager, Metadatable{
* @param int $level 0-15
*/
public function setBlockSkyLightAt(int $x, int $y, int $z, int $level){
$this->getChunk($x >> 4, $z >> 4, true)->setBlockSkyLight($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f, $level & 0x0f);
$this->getChunk($x >> 4, $z >> 4, true)->setBlockSkyLight($x & 0x0f, $y, $z & 0x0f, $level & 0x0f);
}
/**
@ -2103,7 +2129,7 @@ class Level implements ChunkManager, Metadatable{
* @return int 0-15
*/
public function getBlockLightAt(int $x, int $y, int $z) : int{
return $this->getChunk($x >> 4, $z >> 4, true)->getBlockLight($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f);
return $this->getChunk($x >> 4, $z >> 4, true)->getBlockLight($x & 0x0f, $y, $z & 0x0f);
}
/**
@ -2115,7 +2141,7 @@ class Level implements ChunkManager, Metadatable{
* @param int $level 0-15
*/
public function setBlockLightAt(int $x, int $y, int $z, int $level){
$this->getChunk($x >> 4, $z >> 4, true)->setBlockLight($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f, $level & 0x0f);
$this->getChunk($x >> 4, $z >> 4, true)->setBlockLight($x & 0x0f, $y, $z & 0x0f, $level & 0x0f);
}
/**
@ -2763,6 +2789,10 @@ class Level implements ChunkManager, Metadatable{
$this->provider->setSeed($seed);
}
public function getWorldHeight() : int{
return $this->provider->getWorldHeight();
}
public function populateChunk(int $x, int $z, bool $force = false) : bool{
if(isset($this->chunkPopulationQueue[$index = Level::chunkHash($x, $z)]) or (count($this->chunkPopulationQueue) >= $this->chunkPopulationQueueSize and !$force)){

View File

@ -31,9 +31,11 @@ class SimpleChunkManager implements ChunkManager{
protected $chunks = [];
protected $seed;
protected $worldHeight;
public function __construct($seed){
public function __construct($seed, int $worldHeight = Level::Y_MAX){
$this->seed = $seed;
$this->worldHeight = $worldHeight;
}
/**
@ -159,4 +161,16 @@ class SimpleChunkManager implements ChunkManager{
public function getSeed(){
return $this->seed;
}
public function getWorldHeight() : int{
return $this->worldHeight;
}
public function isInWorld(float $x, float $y, float $z) : bool{
return (
$x <= INT32_MAX and $x >= INT32_MIN and
$y < $this->worldHeight and $y >= 0 and
$z <= INT32_MAX and $z >= INT32_MIN
);
}
}

View File

@ -36,18 +36,20 @@ class GeneratorRegisterTask extends AsyncTask{
public $settings;
public $seed;
public $levelId;
public $worldHeight = Level::Y_MAX;
public function __construct(Level $level, Generator $generator){
$this->generator = get_class($generator);
$this->settings = serialize($generator->getSettings());
$this->seed = $level->getSeed();
$this->levelId = $level->getId();
$this->worldHeight = $level->getWorldHeight();
}
public function onRun(){
Block::init();
Biome::init();
$manager = new SimpleChunkManager($this->seed);
$manager = new SimpleChunkManager($this->seed, $this->worldHeight);
$this->saveToThreadStore("generation.level{$this->levelId}.manager", $manager);
/** @var Generator $generator */
$generator = $this->generator;

View File

@ -62,9 +62,14 @@ abstract class LightUpdate{
abstract protected function setLight(int $x, int $y, int $z, int $level);
public function setAndUpdateLight(int $x, int $y, int $z, int $newLevel){
if(!$this->level->isInWorld($x, $y, $z)){
throw new \InvalidArgumentException("Coordinates x=$x, y=$y, z=$z are out of range");
}
if(isset($this->spreadVisited[$index = Level::blockHash($x, $y, $z)]) or isset($this->removalVisited[$index])){
throw new \InvalidArgumentException("Already have a visit ready for this block");
}
$oldLevel = $this->getLight($x, $y, $z);
if($oldLevel !== $newLevel){
@ -93,7 +98,7 @@ abstract class LightUpdate{
];
foreach($points as list($cx, $cy, $cz)){
if($cy < 0){
if(!$this->level->isInWorld($cx, $cy, $cz)){
continue;
}
$this->computeRemoveLight($cx, $cy, $cz, $oldAdjacentLight);
@ -118,7 +123,7 @@ abstract class LightUpdate{
];
foreach($points as list($cx, $cy, $cz)){
if($cy < 0){
if(!$this->level->isInWorld($cx, $cy, $cz)){
continue;
}
$this->computeSpreadLight($cx, $cy, $cz, $newAdjacentLight);