Fixed block update recursion issues (#464)

* Schedule all neighbour block updates to execute at the end of the tick, fixed recursion crash, close #251

* doTickPending timings now include neighbour block update times, refactored some var names
This commit is contained in:
Dylan K. Taylor 2017-04-01 20:18:56 +01:00 committed by GitHub
parent afb2e0c51f
commit 630f0fab7f
4 changed files with 67 additions and 19 deletions

View File

@ -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;
}

View File

@ -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());
}
}

View File

@ -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;
}

View File

@ -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();