mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-22 08:44:01 +00:00
Liquids refactor, bug fixes and performance improvements (#1753)
* Added Liquid->getLiquidLevelDecreasePerBlock() * Fixed lava turning into cobblestone when flowing over water * Cache liquid flow vectors for faster entity movement * Removed a condition that made lava impossible to get rid of In the PC code, the equivalent code makes the delay between scheduled ticks 4 times longer. Here, it just breaks the code. I don't know what the 4x stuff is about, but this code does not produce the expected behaviour and lava works fine regardless. * Fixed strange behaviour with liquids trying to flow into other liquids Liquids should consider other liquids as a path of least resistance. However, they should not actually flow into them. This fixes a variety of CPU leak issues with falling water in large water bodies such as oceans. This also fixes the plus-shape effect that liquid is supposed to produce when a source is placed above ground. * Removed a bad optimization making liquids flowing down slopes behave undesirably * Optimize performance of slope searching by limiting recursion depth based on previous path lengths If we already found a step down on a previous run after 2 blocks, it doesn't make sense to continue allowing checking 4 blocks because the results will just be ignored. This allows limiting the number of recursion steps, which significantly improves the performance when flowing down slopes. However, this will still be just as bad for performance on flat terrain as it was to start with. * Make some Liquid methods only accept Blocks as parameters these are only ever passed blocks anyway, doesn't make sense to allow vectors. * Moved some things to local variables these are each only used in one function, so it doesn't make sense for them to be class members. * Fixed water flow down slopes going everywhere, but degraded performance again * Lava should only search 2 blocks for a slope * Stop wasting CPU calculating optimal flow directions for liquids with too-high decay It calculates the flow directions and THEN doesn't use them when it realizes the flow decay is too high. This is completely pointless. * Use a less hacky method to handle lava flowing into water * Doubled flow performance on flat terrain Since calculateFlowCost() usually ends up visiting the same blocks about 6 times when on flat terrain, it makes sense to cache some stuff for when blocks get revisited so expensive dumb checks don't need to keep on being done. On my machine this produces a 50-60% performance improvement when flowing on flat terrain. * Fixed missing return values in Liquid->onUpdate() these return values aren't used anywhere, but we should be consistent * Don't allow flowing back in the same direction we just came from This reduces the recursions by about 30%, providing about the same percentage performance improvement. * Remove Liquid's temporalVector (it's not used anymore) primitive types for the win! * Move liquid collide to its own method * add sound for lava/water mix
This commit is contained in:
parent
990a48d858
commit
d03fdd5f72
@ -566,10 +566,10 @@ class Block extends Position implements BlockIds, Metadatable{
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears any cached precomputed bounding boxes. This is called on block neighbour update and when the block is set
|
||||
* into the world to remove any outdated precomputed AABBs and force recalculation.
|
||||
* Clears any cached precomputed objects, such as bounding boxes. This is called on block neighbour update and when
|
||||
* the block is set into the world to remove any outdated precomputed things such as AABBs and force recalculation.
|
||||
*/
|
||||
public function clearBoundingBoxes() : void{
|
||||
public function clearCaches() : void{
|
||||
$this->boundingBox = null;
|
||||
$this->collisionBoxes = null;
|
||||
}
|
||||
|
@ -48,6 +48,41 @@ class Lava extends Liquid{
|
||||
return "Lava";
|
||||
}
|
||||
|
||||
public function tickRate() : int{
|
||||
return 30;
|
||||
}
|
||||
|
||||
public function getFlowDecayPerBlock() : int{
|
||||
return 2; //TODO: this is 1 in the nether
|
||||
}
|
||||
|
||||
protected function checkForHarden(){
|
||||
$colliding = null;
|
||||
for($side = 1; $side <= 5; ++$side){ //don't check downwards side
|
||||
$blockSide = $this->getSide($side);
|
||||
if($blockSide instanceof Water){
|
||||
$colliding = $blockSide;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if($colliding !== null){
|
||||
if($this->getDamage() === 0){
|
||||
$this->liquidCollide($colliding, BlockFactory::get(Block::OBSIDIAN));
|
||||
}elseif($this->getDamage() <= 4){
|
||||
$this->liquidCollide($colliding, BlockFactory::get(Block::COBBLESTONE));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function flowIntoBlock(Block $block, int $newFlowDecay) : void{
|
||||
if($block instanceof Water){
|
||||
$block->liquidCollide($this, BlockFactory::get(Block::STONE));
|
||||
}else{
|
||||
parent::flowIntoBlock($block, $newFlowDecay);
|
||||
}
|
||||
}
|
||||
|
||||
public function onEntityCollide(Entity $entity) : void{
|
||||
$entity->fallDistance *= 0.5;
|
||||
|
||||
|
@ -28,11 +28,21 @@ use pocketmine\item\Item;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
|
||||
abstract class Liquid extends Transparent{
|
||||
|
||||
/** @var Vector3 */
|
||||
private $temporalVector = null;
|
||||
public $adjacentSources = 0;
|
||||
|
||||
/** @var Vector3|null */
|
||||
protected $flowVector = null;
|
||||
|
||||
/** @var int[] */
|
||||
private $flowCostVisited = [];
|
||||
|
||||
private const CAN_FLOW_DOWN = 1;
|
||||
private const CAN_FLOW = 0;
|
||||
private const BLOCKED = -1;
|
||||
|
||||
public function hasEntityCollision() : bool{
|
||||
return true;
|
||||
@ -46,13 +56,25 @@ abstract class Liquid extends Transparent{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function canBeFlowedInto() : bool{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function isSolid() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public $adjacentSources = 0;
|
||||
public $isOptimalFlowDirection = [0, 0, 0, 0];
|
||||
public $flowCost = [0, 0, 0, 0];
|
||||
public function getHardness() : float{
|
||||
return 100;
|
||||
}
|
||||
|
||||
protected function recalculateBoundingBox() : ?AxisAlignedBB{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getDrops(Item $item) : array{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getFluidHeightPercent(){
|
||||
$d = $this->meta;
|
||||
@ -63,28 +85,20 @@ abstract class Liquid extends Transparent{
|
||||
return ($d + 1) / 9;
|
||||
}
|
||||
|
||||
protected function getFlowDecay(Vector3 $pos){
|
||||
if(!($pos instanceof Block)){
|
||||
$pos = $this->level->getBlock($pos);
|
||||
protected function getFlowDecay(Block $block) : int{
|
||||
if($block->getId() !== $this->getId()){
|
||||
return -1;
|
||||
}
|
||||
|
||||
if($pos->getId() !== $this->getId()){
|
||||
return -1;
|
||||
}else{
|
||||
return $pos->getDamage();
|
||||
}
|
||||
return $block->getDamage();
|
||||
}
|
||||
|
||||
protected function getEffectiveFlowDecay(Vector3 $pos){
|
||||
if(!($pos instanceof Block)){
|
||||
$pos = $this->level->getBlock($pos);
|
||||
}
|
||||
|
||||
if($pos->getId() !== $this->getId()){
|
||||
protected function getEffectiveFlowDecay(Block $block) : int{
|
||||
if($block->getId() !== $this->getId()){
|
||||
return -1;
|
||||
}
|
||||
|
||||
$decay = $pos->getDamage();
|
||||
$decay = $block->getDamage();
|
||||
|
||||
if($decay >= 8){
|
||||
$decay = 0;
|
||||
@ -93,13 +107,18 @@ abstract class Liquid extends Transparent{
|
||||
return $decay;
|
||||
}
|
||||
|
||||
public function getFlowVector(){
|
||||
$vector = new Vector3(0, 0, 0);
|
||||
public function clearCaches() : void{
|
||||
parent::clearCaches();
|
||||
$this->flowVector = null;
|
||||
}
|
||||
|
||||
if($this->temporalVector === null){
|
||||
$this->temporalVector = new Vector3(0, 0, 0);
|
||||
public function getFlowVector() : Vector3{
|
||||
if($this->flowVector !== null){
|
||||
return $this->flowVector;
|
||||
}
|
||||
|
||||
$vector = new Vector3(0, 0, 0);
|
||||
|
||||
$decay = $this->getEffectiveFlowDecay($this);
|
||||
|
||||
for($j = 0; $j < 4; ++$j){
|
||||
@ -144,32 +163,21 @@ abstract class Liquid extends Transparent{
|
||||
}
|
||||
|
||||
if($this->getDamage() >= 8){
|
||||
$falling = false;
|
||||
|
||||
if(!$this->level->getBlockAt($this->x, $this->y, $this->z - 1)->canBeFlowedInto()){
|
||||
$falling = true;
|
||||
}elseif(!$this->level->getBlockAt($this->x, $this->y, $this->z + 1)->canBeFlowedInto()){
|
||||
$falling = true;
|
||||
}elseif(!$this->level->getBlockAt($this->x - 1, $this->y, $this->z)->canBeFlowedInto()){
|
||||
$falling = true;
|
||||
}elseif(!$this->level->getBlockAt($this->x + 1, $this->y, $this->z)->canBeFlowedInto()){
|
||||
$falling = true;
|
||||
}elseif(!$this->level->getBlockAt($this->x, $this->y + 1, $this->z - 1)->canBeFlowedInto()){
|
||||
$falling = true;
|
||||
}elseif(!$this->level->getBlockAt($this->x, $this->y + 1, $this->z + 1)->canBeFlowedInto()){
|
||||
$falling = true;
|
||||
}elseif(!$this->level->getBlockAt($this->x - 1, $this->y + 1, $this->z)->canBeFlowedInto()){
|
||||
$falling = true;
|
||||
}elseif(!$this->level->getBlockAt($this->x + 1, $this->y + 1, $this->z)->canBeFlowedInto()){
|
||||
$falling = true;
|
||||
}
|
||||
|
||||
if($falling){
|
||||
if(
|
||||
!$this->canFlowInto($this->level->getBlockAt($this->x, $this->y, $this->z - 1)) or
|
||||
!$this->canFlowInto($this->level->getBlockAt($this->x, $this->y, $this->z + 1)) or
|
||||
!$this->canFlowInto($this->level->getBlockAt($this->x - 1, $this->y, $this->z)) or
|
||||
!$this->canFlowInto($this->level->getBlockAt($this->x + 1, $this->y, $this->z)) or
|
||||
!$this->canFlowInto($this->level->getBlockAt($this->x, $this->y + 1, $this->z - 1)) or
|
||||
!$this->canFlowInto($this->level->getBlockAt($this->x, $this->y + 1, $this->z + 1)) or
|
||||
!$this->canFlowInto($this->level->getBlockAt($this->x - 1, $this->y + 1, $this->z)) or
|
||||
!$this->canFlowInto($this->level->getBlockAt($this->x + 1, $this->y + 1, $this->z))
|
||||
){
|
||||
$vector = $vector->normalize()->add(0, -6, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return $vector->normalize();
|
||||
return $this->flowVector = $vector->normalize();
|
||||
}
|
||||
|
||||
public function addVelocityToEntity(Entity $entity, Vector3 $vector) : void{
|
||||
@ -179,29 +187,33 @@ abstract class Liquid extends Transparent{
|
||||
$vector->z += $flow->z;
|
||||
}
|
||||
|
||||
public function tickRate(){
|
||||
if($this instanceof Water){
|
||||
return 5;
|
||||
}elseif($this instanceof Lava){
|
||||
return 30;
|
||||
}
|
||||
abstract public function tickRate() : int;
|
||||
|
||||
return 0;
|
||||
/**
|
||||
* Returns how many liquid levels are lost per block flowed horizontally. Affects how far the liquid can flow.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getFlowDecayPerBlock() : int{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param int $type
|
||||
*
|
||||
* @return bool|int
|
||||
*/
|
||||
public function onUpdate(int $type){
|
||||
if($type === Level::BLOCK_UPDATE_NORMAL){
|
||||
$this->checkForHarden();
|
||||
$this->level->scheduleDelayedBlockUpdate($this, $this->tickRate());
|
||||
|
||||
return $type;
|
||||
}elseif($type === Level::BLOCK_UPDATE_SCHEDULED){
|
||||
if($this->temporalVector === null){
|
||||
$this->temporalVector = new Vector3(0, 0, 0);
|
||||
}
|
||||
|
||||
$decay = $this->getFlowDecay($this);
|
||||
$multiplier = $this instanceof Lava ? 2 : 1;
|
||||
|
||||
$flag = true;
|
||||
$multiplier = $this->getFlowDecayPerBlock();
|
||||
|
||||
if($decay > 0){
|
||||
$smallestFlowDecay = -100;
|
||||
@ -211,99 +223,80 @@ abstract class Liquid extends Transparent{
|
||||
$smallestFlowDecay = $this->getSmallestFlowDecay($this->level->getBlockAt($this->x - 1, $this->y, $this->z), $smallestFlowDecay);
|
||||
$smallestFlowDecay = $this->getSmallestFlowDecay($this->level->getBlockAt($this->x + 1, $this->y, $this->z), $smallestFlowDecay);
|
||||
|
||||
$k = $smallestFlowDecay + $multiplier;
|
||||
$newDecay = $smallestFlowDecay + $multiplier;
|
||||
|
||||
if($k >= 8 or $smallestFlowDecay < 0){
|
||||
$k = -1;
|
||||
if($newDecay >= 8 or $smallestFlowDecay < 0){
|
||||
$newDecay = -1;
|
||||
}
|
||||
|
||||
if(($topFlowDecay = $this->getFlowDecay($this->level->getBlockAt($this->x, $this->y + 1, $this->z))) >= 0){
|
||||
if($topFlowDecay >= 8){
|
||||
$k = $topFlowDecay;
|
||||
}else{
|
||||
$k = $topFlowDecay | 0x08;
|
||||
}
|
||||
$newDecay = $topFlowDecay | 0x08;
|
||||
}
|
||||
|
||||
if($this->adjacentSources >= 2 and $this instanceof Water){
|
||||
$bottomBlock = $this->level->getBlockAt($this->x, $this->y - 1, $this->z);
|
||||
if($bottomBlock->isSolid()){
|
||||
$k = 0;
|
||||
$newDecay = 0;
|
||||
}elseif($bottomBlock instanceof Water and $bottomBlock->getDamage() === 0){
|
||||
$k = 0;
|
||||
$newDecay = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if($this instanceof Lava and $decay < 8 and $k < 8 and $k > 1 and mt_rand(0, 4) !== 0){
|
||||
$k = $decay;
|
||||
$flag = false;
|
||||
}
|
||||
|
||||
if($k !== $decay){
|
||||
$decay = $k;
|
||||
if($newDecay !== $decay){
|
||||
$decay = $newDecay;
|
||||
if($decay < 0){
|
||||
$this->level->setBlock($this, BlockFactory::get(Block::AIR), true, true);
|
||||
}else{
|
||||
$this->level->setBlock($this, BlockFactory::get($this->id, $decay), true, true);
|
||||
$this->level->scheduleDelayedBlockUpdate($this, $this->tickRate());
|
||||
}
|
||||
}elseif($flag){
|
||||
//$this->getLevel()->scheduleUpdate($this, $this->tickRate());
|
||||
//$this->updateFlow();
|
||||
}
|
||||
}else{
|
||||
//$this->updateFlow();
|
||||
}
|
||||
|
||||
if($decay >= 0){
|
||||
$bottomBlock = $this->level->getBlockAt($this->x, $this->y - 1, $this->z);
|
||||
|
||||
if($this instanceof Lava and $bottomBlock instanceof Water){
|
||||
$this->level->setBlock($bottomBlock, BlockFactory::get(Block::STONE), true, true);
|
||||
|
||||
}elseif($bottomBlock->canBeFlowedInto() or ($bottomBlock instanceof Liquid and ($bottomBlock->getDamage() & 0x07) !== 0)){
|
||||
$this->level->setBlock($bottomBlock, BlockFactory::get($this->id, $decay | 0x08), true, false);
|
||||
$this->level->scheduleDelayedBlockUpdate($bottomBlock, $this->tickRate());
|
||||
|
||||
}elseif($decay === 0 or !$bottomBlock->canBeFlowedInto()){
|
||||
$flags = $this->getOptimalFlowDirections();
|
||||
|
||||
$l = $decay + $multiplier;
|
||||
$this->flowIntoBlock($bottomBlock, $decay | 0x08);
|
||||
|
||||
if($decay === 0 or !$bottomBlock->canBeFlowedInto()){
|
||||
if($decay >= 8){
|
||||
$l = 1;
|
||||
$adjacentDecay = 1;
|
||||
}else{
|
||||
$adjacentDecay = $decay + $multiplier;
|
||||
}
|
||||
|
||||
if($l >= 8){
|
||||
$this->checkForHarden();
|
||||
if($adjacentDecay < 8){
|
||||
$flags = $this->getOptimalFlowDirections();
|
||||
|
||||
return;
|
||||
}
|
||||
if($flags[0]){
|
||||
$this->flowIntoBlock($this->level->getBlockAt($this->x - 1, $this->y, $this->z), $adjacentDecay);
|
||||
}
|
||||
|
||||
if($flags[0]){
|
||||
$this->flowIntoBlock($this->level->getBlockAt($this->x - 1, $this->y, $this->z), $l);
|
||||
}
|
||||
if($flags[1]){
|
||||
$this->flowIntoBlock($this->level->getBlockAt($this->x + 1, $this->y, $this->z), $adjacentDecay);
|
||||
}
|
||||
|
||||
if($flags[1]){
|
||||
$this->flowIntoBlock($this->level->getBlockAt($this->x + 1, $this->y, $this->z), $l);
|
||||
}
|
||||
if($flags[2]){
|
||||
$this->flowIntoBlock($this->level->getBlockAt($this->x, $this->y, $this->z - 1), $adjacentDecay);
|
||||
}
|
||||
|
||||
if($flags[2]){
|
||||
$this->flowIntoBlock($this->level->getBlockAt($this->x, $this->y, $this->z - 1), $l);
|
||||
}
|
||||
|
||||
if($flags[3]){
|
||||
$this->flowIntoBlock($this->level->getBlockAt($this->x, $this->y, $this->z + 1), $l);
|
||||
if($flags[3]){
|
||||
$this->flowIntoBlock($this->level->getBlockAt($this->x, $this->y, $this->z + 1), $adjacentDecay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->checkForHarden();
|
||||
}
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function flowIntoBlock(Block $block, $newFlowDecay){
|
||||
if($block->canBeFlowedInto()){
|
||||
protected function flowIntoBlock(Block $block, int $newFlowDecay) : void{
|
||||
if($this->canFlowInto($block) and !($block instanceof Liquid)){
|
||||
if($block->getId() > 0){
|
||||
$this->level->useBreakOn($block);
|
||||
}
|
||||
@ -313,66 +306,68 @@ abstract class Liquid extends Transparent{
|
||||
}
|
||||
}
|
||||
|
||||
private function calculateFlowCost(Block $block, $accumulatedCost, $previousDirection){
|
||||
private function calculateFlowCost(int $blockX, int $blockY, int $blockZ, int $accumulatedCost, int $maxCost, int $originOpposite, int $lastOpposite) : int{
|
||||
$cost = 1000;
|
||||
|
||||
for($j = 0; $j < 4; ++$j){
|
||||
if(
|
||||
($j === 0 and $previousDirection === 1) or
|
||||
($j === 1 and $previousDirection === 0) or
|
||||
($j === 2 and $previousDirection === 3) or
|
||||
($j === 3 and $previousDirection === 2)
|
||||
){
|
||||
$x = $block->x;
|
||||
$y = $block->y;
|
||||
$z = $block->z;
|
||||
if($j === $originOpposite or $j === $lastOpposite){
|
||||
continue;
|
||||
}
|
||||
|
||||
if($j === 0){
|
||||
--$x;
|
||||
}elseif($j === 1){
|
||||
++$x;
|
||||
}elseif($j === 2){
|
||||
--$z;
|
||||
}elseif($j === 3){
|
||||
++$z;
|
||||
}
|
||||
$x = $blockX;
|
||||
$y = $blockY;
|
||||
$z = $blockZ;
|
||||
|
||||
if($j === 0){
|
||||
--$x;
|
||||
}elseif($j === 1){
|
||||
++$x;
|
||||
}elseif($j === 2){
|
||||
--$z;
|
||||
}elseif($j === 3){
|
||||
++$z;
|
||||
}
|
||||
|
||||
if(!isset($this->flowCostVisited[$hash = Level::blockHash($x, $y, $z)])){
|
||||
$blockSide = $this->level->getBlockAt($x, $y, $z);
|
||||
|
||||
if(!$blockSide->canBeFlowedInto() and !($blockSide instanceof Liquid)){
|
||||
continue;
|
||||
}elseif($blockSide instanceof Liquid and $blockSide->getDamage() === 0){
|
||||
continue;
|
||||
if(!$this->canFlowInto($blockSide)){
|
||||
$this->flowCostVisited[$hash] = self::BLOCKED;
|
||||
}elseif($this->level->getBlockAt($x, $y - 1, $z)->canBeFlowedInto()){
|
||||
return $accumulatedCost;
|
||||
$this->flowCostVisited[$hash] = self::CAN_FLOW_DOWN;
|
||||
}else{
|
||||
$this->flowCostVisited[$hash] = self::CAN_FLOW;
|
||||
}
|
||||
}
|
||||
|
||||
if($accumulatedCost >= 4){
|
||||
continue;
|
||||
}
|
||||
$status = $this->flowCostVisited[$hash];
|
||||
|
||||
$realCost = $this->calculateFlowCost($blockSide, $accumulatedCost + 1, $j);
|
||||
if($status === self::BLOCKED){
|
||||
continue;
|
||||
}elseif($status === self::CAN_FLOW_DOWN){
|
||||
return $accumulatedCost;
|
||||
}
|
||||
|
||||
if($realCost < $cost){
|
||||
$cost = $realCost;
|
||||
}
|
||||
if($accumulatedCost >= $maxCost){
|
||||
continue;
|
||||
}
|
||||
|
||||
$realCost = $this->calculateFlowCost($x, $y, $z, $accumulatedCost + 1, $maxCost, $originOpposite, $j ^ 0x01);
|
||||
|
||||
if($realCost < $cost){
|
||||
$cost = $realCost;
|
||||
}
|
||||
}
|
||||
|
||||
return $cost;
|
||||
}
|
||||
|
||||
public function getHardness() : float{
|
||||
return 100;
|
||||
}
|
||||
|
||||
private function getOptimalFlowDirections(){
|
||||
if($this->temporalVector === null){
|
||||
$this->temporalVector = new Vector3(0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool[]
|
||||
*/
|
||||
private function getOptimalFlowDirections() : array{
|
||||
$flowCost = array_fill(0, 4, 1000);
|
||||
$maxCost = 4 / $this->getFlowDecayPerBlock();
|
||||
for($j = 0; $j < 4; ++$j){
|
||||
$this->flowCost[$j] = 1000;
|
||||
|
||||
$x = $this->x;
|
||||
$y = $this->y;
|
||||
$z = $this->z;
|
||||
@ -388,34 +383,34 @@ abstract class Liquid extends Transparent{
|
||||
}
|
||||
$block = $this->level->getBlockAt($x, $y, $z);
|
||||
|
||||
if(!$block->canBeFlowedInto() and !($block instanceof Liquid)){
|
||||
continue;
|
||||
}elseif($block instanceof Liquid and $block->getDamage() === 0){
|
||||
if(!$this->canFlowInto($block)){
|
||||
$this->flowCostVisited[Level::blockHash($x, $y, $z)] = self::BLOCKED;
|
||||
continue;
|
||||
}elseif($this->level->getBlockAt($x, $y - 1, $z)->canBeFlowedInto()){
|
||||
$this->flowCost[$j] = 0;
|
||||
}else{
|
||||
$this->flowCost[$j] = $this->calculateFlowCost($block, 1, $j);
|
||||
$this->flowCostVisited[Level::blockHash($x, $y, $z)] = self::CAN_FLOW_DOWN;
|
||||
$flowCost[$j] = $maxCost = 0;
|
||||
}elseif($maxCost > 0){
|
||||
$this->flowCostVisited[Level::blockHash($x, $y, $z)] = self::CAN_FLOW;
|
||||
$flowCost[$j] = $this->calculateFlowCost($x, $y, $z, 1, $maxCost, $j ^ 0x01, $j ^ 0x01);
|
||||
$maxCost = min($maxCost, $flowCost[$j]);
|
||||
}
|
||||
}
|
||||
|
||||
$minCost = $this->flowCost[0];
|
||||
$this->flowCostVisited = [];
|
||||
|
||||
for($i = 1; $i < 4; ++$i){
|
||||
if($this->flowCost[$i] < $minCost){
|
||||
$minCost = $this->flowCost[$i];
|
||||
}
|
||||
}
|
||||
$minCost = min($flowCost);
|
||||
|
||||
$isOptimalFlowDirection = [];
|
||||
|
||||
for($i = 0; $i < 4; ++$i){
|
||||
$this->isOptimalFlowDirection[$i] = ($this->flowCost[$i] === $minCost);
|
||||
$isOptimalFlowDirection[$i] = ($flowCost[$i] === $minCost);
|
||||
}
|
||||
|
||||
return $this->isOptimalFlowDirection;
|
||||
return $isOptimalFlowDirection;
|
||||
}
|
||||
|
||||
private function getSmallestFlowDecay(Vector3 $pos, $decay){
|
||||
$blockDecay = $this->getFlowDecay($pos);
|
||||
private function getSmallestFlowDecay(Block $block, int $decay) : int{
|
||||
$blockDecay = $this->getFlowDecay($block);
|
||||
|
||||
if($blockDecay < 0){
|
||||
return $decay;
|
||||
@ -428,28 +423,19 @@ abstract class Liquid extends Transparent{
|
||||
return ($decay >= 0 && $blockDecay >= $decay) ? $decay : $blockDecay;
|
||||
}
|
||||
|
||||
private function checkForHarden(){
|
||||
if($this instanceof Lava){
|
||||
$colliding = false;
|
||||
for($side = 0; $side <= 5 and !$colliding; ++$side){
|
||||
$colliding = $this->getSide($side) instanceof Water;
|
||||
}
|
||||
protected function checkForHarden(){
|
||||
|
||||
if($colliding){
|
||||
if($this->getDamage() === 0){
|
||||
$this->level->setBlock($this, BlockFactory::get(Block::OBSIDIAN), true, true);
|
||||
}elseif($this->getDamage() <= 4){
|
||||
$this->level->setBlock($this, BlockFactory::get(Block::COBBLESTONE), true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function recalculateBoundingBox() : ?AxisAlignedBB{
|
||||
return null;
|
||||
protected function liquidCollide(Block $cause, Block $result) : bool{
|
||||
//TODO: add events
|
||||
|
||||
$this->level->setBlock($this, $result, true, true);
|
||||
$this->level->broadcastLevelSoundEvent($this->add(0.5, 0.5, 0.5), LevelSoundEventPacket::SOUND_FIZZ, (int) ((2.6 + (lcg_value() - lcg_value()) * 0.8) * 1000));
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getDrops(Item $item) : array{
|
||||
return [];
|
||||
protected function canFlowInto(Block $block) : bool{
|
||||
return $block->canBeFlowedInto() and !($block instanceof Liquid and $block->meta === 0); //TODO: I think this should only be liquids of the same type
|
||||
}
|
||||
}
|
@ -44,6 +44,10 @@ class Water extends Liquid{
|
||||
return 2;
|
||||
}
|
||||
|
||||
public function tickRate() : int{
|
||||
return 5;
|
||||
}
|
||||
|
||||
public function onEntityCollide(Entity $entity) : void{
|
||||
$entity->resetFallDistance();
|
||||
if($entity->fireTicks > 0){
|
||||
|
@ -719,7 +719,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
Level::getBlockXYZ($index, $x, $y, $z);
|
||||
|
||||
$block = $this->getBlockAt($x, $y, $z);
|
||||
$block->clearBoundingBoxes(); //for blocks like fences, force recalculation of connected AABBs
|
||||
$block->clearCaches(); //for blocks like fences, force recalculation of connected AABBs
|
||||
|
||||
$this->server->getPluginManager()->callEvent($ev = new BlockUpdateEvent($block));
|
||||
if(!$ev->isCancelled()){
|
||||
@ -1512,7 +1512,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
|
||||
$block->position($pos);
|
||||
$block->clearBoundingBoxes();
|
||||
$block->clearCaches();
|
||||
unset($this->blockCache[Level::blockHash($pos->x, $pos->y, $pos->z)]);
|
||||
|
||||
$index = Level::chunkHash($pos->x >> 4, $pos->z >> 4);
|
||||
|
Loading…
x
Reference in New Issue
Block a user