diff --git a/src/pocketmine/block/Lava.php b/src/pocketmine/block/Lava.php index d90f34ad3..d0d07042b 100644 --- a/src/pocketmine/block/Lava.php +++ b/src/pocketmine/block/Lava.php @@ -64,7 +64,7 @@ class Lava extends Liquid{ public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $ret = $this->getLevel()->setBlock($this, $this, true, false); - $this->getLevel()->scheduleUpdate($this, $this->tickRate()); + $this->getLevel()->scheduleDelayedBlockUpdate($this, $this->tickRate()); return $ret; } diff --git a/src/pocketmine/block/Liquid.php b/src/pocketmine/block/Liquid.php index 8c0f333b8..79e6730cd 100644 --- a/src/pocketmine/block/Liquid.php +++ b/src/pocketmine/block/Liquid.php @@ -189,7 +189,7 @@ abstract class Liquid extends Transparent{ public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ $this->checkForHarden(); - $this->getLevel()->scheduleUpdate($this, $this->tickRate()); + $this->getLevel()->scheduleDelayedBlockUpdate($this, $this->tickRate()); }elseif($type === Level::BLOCK_UPDATE_SCHEDULED){ if($this->temporalVector === null){ $this->temporalVector = new Vector3(0, 0, 0); @@ -242,7 +242,7 @@ abstract class Liquid extends Transparent{ $this->getLevel()->setBlock($this, new Air(), true); }else{ $this->getLevel()->setBlock($this, Block::get($this->id, $decay), true); - $this->getLevel()->scheduleUpdate($this, $this->tickRate()); + $this->getLevel()->scheduleDelayedBlockUpdate($this, $this->tickRate()); } }elseif($flag){ //$this->getLevel()->scheduleUpdate($this, $this->tickRate()); @@ -262,10 +262,10 @@ abstract class Liquid extends Transparent{ if($decay >= 8){ $this->getLevel()->setBlock($bottomBlock, Block::get($this->id, $decay), true); - $this->getLevel()->scheduleUpdate($bottomBlock, $this->tickRate()); + $this->getLevel()->scheduleDelayedBlockUpdate($bottomBlock, $this->tickRate()); }else{ $this->getLevel()->setBlock($bottomBlock, Block::get($this->id, $decay + 8), true); - $this->getLevel()->scheduleUpdate($bottomBlock, $this->tickRate()); + $this->getLevel()->scheduleDelayedBlockUpdate($bottomBlock, $this->tickRate()); } }elseif($decay >= 0 and ($decay === 0 or !$bottomBlock->canBeFlowedInto())){ $flags = $this->getOptimalFlowDirections(); @@ -310,7 +310,7 @@ abstract class Liquid extends Transparent{ } $this->getLevel()->setBlock($block, Block::get($this->getId(), $newFlowDecay), true); - $this->getLevel()->scheduleUpdate($block, $this->tickRate()); + $this->getLevel()->scheduleDelayedBlockUpdate($block, $this->tickRate()); } } diff --git a/src/pocketmine/block/Water.php b/src/pocketmine/block/Water.php index 4e7da94a5..d71e4791a 100644 --- a/src/pocketmine/block/Water.php +++ b/src/pocketmine/block/Water.php @@ -48,7 +48,7 @@ class Water extends Liquid{ public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $ret = $this->getLevel()->setBlock($this, $this, true, false); - $this->getLevel()->scheduleUpdate($this, $this->tickRate()); + $this->getLevel()->scheduleDelayedBlockUpdate($this, $this->tickRate()); return $ret; } diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index d8a456455..a8677ea12 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -193,8 +193,11 @@ class Level implements ChunkManager, Metadatable{ private $changedBlocks = []; /** @var ReversePriorityQueue */ - private $updateQueue; - private $updateQueueIndex = []; + private $scheduledBlockUpdateQueue; + private $scheduledBlockUpdateQueueIndex = []; + + /** @var \SplQueue */ + private $neighbourBlockUpdateQueue = []; /** @var Player[][] */ private $chunkSendQueue = []; @@ -336,8 +339,11 @@ class Level implements ChunkManager, Metadatable{ $this->generator = Generator::getGenerator($this->provider->getGenerator()); $this->folderName = $name; - $this->updateQueue = new ReversePriorityQueue(); - $this->updateQueue->setExtractFlags(\SplPriorityQueue::EXTR_BOTH); + $this->scheduledBlockUpdateQueue = new ReversePriorityQueue(); + $this->scheduledBlockUpdateQueue->setExtractFlags(\SplPriorityQueue::EXTR_BOTH); + + $this->neighbourBlockUpdateQueue = new \SplQueue(); + $this->time = (int) $this->provider->getTime(); $this->chunkTickRadius = min($this->server->getViewDistance(), max(1, (int) $this->server->getProperty("chunk-ticking.tick-radius", 4))); @@ -660,11 +666,24 @@ class Level implements ChunkManager, Metadatable{ //Do block updates $this->timings->doTickPending->startTiming(); - while($this->updateQueue->count() > 0 and $this->updateQueue->current()["priority"] <= $currentTick){ - $block = $this->getBlock($this->updateQueue->extract()["data"]); - unset($this->updateQueueIndex[Level::blockHash($block->x, $block->y, $block->z)]); + + //Delayed updates + while($this->scheduledBlockUpdateQueue->count() > 0 and $this->scheduledBlockUpdateQueue->current()["priority"] <= $currentTick){ + $block = $this->getBlock($this->scheduledBlockUpdateQueue->extract()["data"]); + unset($this->scheduledBlockUpdateQueueIndex[Level::blockHash($block->x, $block->y, $block->z)]); $block->onUpdate(self::BLOCK_UPDATE_SCHEDULED); } + + //Normal updates + while($this->neighbourBlockUpdateQueue->count() > 0){ + $index = $this->neighbourBlockUpdateQueue->dequeue(); + Level::getBlockXYZ($index, $x, $y, $z); + $this->server->getPluginManager()->callEvent($ev = new BlockUpdateEvent($this->getBlock($this->temporalVector->setComponents($x, $y, $z)))); + if(!$ev->isCancelled()){ + $ev->getBlock()->onUpdate(self::BLOCK_UPDATE_NORMAL); + } + } + $this->timings->doTickPending->stopTiming(); $this->timings->entityTick->startTiming(); @@ -1044,15 +1063,45 @@ class Level implements ChunkManager, Metadatable{ } /** + * @deprecated This method will be removed in the future due to misleading/ambiguous name. Use {@link Level#scheduleDelayedBlockUpdate} instead. + * * @param Vector3 $pos * @param int $delay */ public function scheduleUpdate(Vector3 $pos, int $delay){ - if(isset($this->updateQueueIndex[$index = Level::blockHash($pos->x, $pos->y, $pos->z)]) and $this->updateQueueIndex[$index] <= $delay){ + $this->scheduleDelayedBlockUpdate($pos, $delay); + } + + /** + * Schedules a block update to be executed after the specified number of ticks. + * Blocks will be updated with the scheduled update type. + * + * @param Vector3 $pos + * @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){ return; } - $this->updateQueueIndex[$index] = $delay; - $this->updateQueue->insert(new Vector3((int) $pos->x, (int) $pos->y, (int) $pos->z), (int) $delay + $this->server->getTick()); + $this->scheduledBlockUpdateQueueIndex[$index] = $delay; + $this->scheduledBlockUpdateQueue->insert(new Vector3((int) $pos->x, (int) $pos->y, (int) $pos->z), (int) $delay + $this->server->getTick()); + } + + /** + * Schedules the blocks around the specified position to be updated at the end of this tick. + * Blocks will be updated with the normal update type. + * + * @param Vector3 $pos + */ + 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)); } /** @@ -1466,9 +1515,8 @@ class Level implements ChunkManager, Metadatable{ $entity->scheduleUpdate(); } $ev->getBlock()->onUpdate(self::BLOCK_UPDATE_NORMAL); + $this->scheduleNeighbourBlockUpdates($pos); } - - $this->updateAround($pos); } $this->timings->setBlock->stopTiming();