Implemented sky light generation-time population and updating, obsolete and close #160

This commit is contained in:
Dylan K. Taylor 2017-04-18 13:05:01 +01:00
parent 5e6a0e7ba0
commit dab73d8950
13 changed files with 417 additions and 154 deletions

View File

@ -54,6 +54,8 @@ class Block extends Position implements BlockIds, Metadatable{
public static $hardness = null;
/** @var \SplFixedArray */
public static $transparent = null;
/** @var \SplFixedArray */
public static $diffusesSkyLight = null;
protected $id;
protected $meta = 0;
@ -70,6 +72,7 @@ class Block extends Position implements BlockIds, Metadatable{
self::$solid = new \SplFixedArray(256);
self::$hardness = new \SplFixedArray(256);
self::$transparent = new \SplFixedArray(256);
self::$diffusesSkyLight = new \SplFixedArray(256);
self::$list[self::AIR] = Air::class;
self::$list[self::STONE] = Stone::class;
self::$list[self::GRASS] = Grass::class;
@ -254,31 +257,20 @@ class Block extends Position implements BlockIds, Metadatable{
for($data = 0; $data < 16; ++$data){
self::$fullList[($id << 4) | $data] = new $class($data);
}
self::$solid[$id] = $block->isSolid();
self::$transparent[$id] = $block->isTransparent();
self::$hardness[$id] = $block->getHardness();
self::$light[$id] = $block->getLightLevel();
if($block->isSolid()){
if($block->isTransparent()){
if($block instanceof Liquid or $block instanceof Ice){
self::$lightFilter[$id] = 2;
}else{
self::$lightFilter[$id] = 1;
}
}else{
self::$lightFilter[$id] = 15;
}
}else{
self::$lightFilter[$id] = 1;
}
}else{
self::$lightFilter[$id] = 1;
$block = new UnknownBlock($id);
for($data = 0; $data < 16; ++$data){
self::$fullList[($id << 4) | $data] = new UnknownBlock($id, $data);
}
}
self::$solid[$id] = $block->isSolid();
self::$transparent[$id] = $block->isTransparent();
self::$hardness[$id] = $block->getHardness();
self::$light[$id] = $block->getLightLevel();
self::$lightFilter[$id] = $block->getLightFilter() + 1;
self::$diffusesSkyLight[$id] = $block->diffusesSkyLight();
}
}
}
@ -419,6 +411,29 @@ class Block extends Position implements BlockIds, Metadatable{
return 0;
}
/**
* Returns the amount of light this block will filter out when light passes through this block.
* This value is used in light spread calculation.
*
* @return int 0-15
*/
public function getLightFilter() : int{
return 15;
}
/**
* Returns whether this block will diffuse sky light passing through it vertically.
* Diffusion means that full-strength sky light passing through this block will not be reduced, but will start being filtered below the block.
* Examples of this behaviour include leaves and cobwebs.
*
* Light-diffusing blocks are included by the heightmap.
*
* @return bool
*/
public function diffusesSkyLight() : bool{
return false;
}
/**
* AKA: Block->isPlaceable
*

View File

@ -57,4 +57,8 @@ class Cobweb extends Flowable{
//TODO: correct drops
return [];
}
public function diffusesSkyLight() : bool{
return true;
}
}

View File

@ -40,6 +40,10 @@ class Ice extends Transparent{
return 0.5;
}
public function getLightFilter() : int{
return 2;
}
public function getToolType(){
return Tool::TYPE_PICKAXE;
}

View File

@ -60,6 +60,10 @@ class Leaves extends Transparent{
return $names[$this->meta & 0x03];
}
public function diffusesSkyLight() : bool{
return true;
}
private function findLog(Block $pos, array $visited, $distance, &$check, $fromSide = null){
++$check;
$index = $pos->x . "." . $pos->y . "." . $pos->z;

View File

@ -27,4 +27,8 @@ abstract class Transparent extends Block{
public function isTransparent(){
return true;
}
public function getLightFilter() : int{
return 0;
}
}

View File

@ -37,6 +37,10 @@ class Water extends Liquid{
return "Water";
}
public function getLightFilter() : int{
return 2;
}
public function onEntityCollide(Entity $entity){
$entity->resetFallDistance();
if($entity->fireTicks > 0){

View File

@ -0,0 +1,34 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
namespace pocketmine\level;
class BlockLightUpdate extends LightUpdate{
public function getLight(int $x, int $y, int $z) : int{
return $this->level->getBlockLightAt($x, $y, $z);
}
public function setLight(int $x, int $y, int $z, int $level){
$this->level->setBlockLightAt($x, $y, $z, $level);
}
}

View File

@ -1253,10 +1253,10 @@ class Level implements ChunkManager, Metadatable{
$chunk = $this->getChunk($pos->x >> 4, $pos->z >> 4, false);
$level = 0;
if($chunk !== null){
$level = $chunk->getBlockSkyLight($pos->x & 0x0f, $pos->y & Level::Y_MASK, $pos->z & 0x0f);
$level = $chunk->getBlockSkyLight($pos->x & 0x0f, $pos->y, $pos->z & 0x0f);
//TODO: decrease light level by time of day
if($level < 15){
$level = max($chunk->getBlockLight($pos->x & 0x0f, $pos->y & Level::Y_MASK, $pos->z & 0x0f));
$level = max($chunk->getBlockLight($pos->x & 0x0f, $pos->y, $pos->z & 0x0f), $level);
}
}
@ -1271,7 +1271,7 @@ class Level implements ChunkManager, Metadatable{
* @return int bitmap, (id << 4) | data
*/
public function getFullBlock(int $x, int $y, int $z) : int{
return $this->getChunk($x >> 4, $z >> 4, false)->getFullBlock($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f);
return $this->getChunk($x >> 4, $z >> 4, false)->getFullBlock($x & 0x0f, $y, $z & 0x0f);
}
/**
@ -1308,14 +1308,64 @@ class Level implements ChunkManager, Metadatable{
$this->updateBlockLight($pos->x, $pos->y, $pos->z);
}
/**
* Returns the highest block light level available in the positions adjacent to the specified block coordinates.
*
* @param int $x
* @param int $y
* @param int $z
*
* @return int
*/
public function getHighestAdjacentBlockSkyLight(int $x, int $y, int $z) : int{
return max([
$this->getBlockSkyLightAt($x + 1, $y, $z),
$this->getBlockSkyLightAt($x - 1, $y, $z),
$this->getBlockSkyLightAt($x, $y + 1, $z),
$this->getBlockSkyLightAt($x, $y - 1, $z),
$this->getBlockSkyLightAt($x, $y, $z + 1),
$this->getBlockSkyLightAt($x, $y, $z - 1)
]);
}
public function updateBlockSkyLight(int $x, int $y, int $z){
$this->timings->doBlockSkyLightUpdates->startTiming();
//TODO
$oldHeightMap = $this->getHeightMap($x, $z);
$sourceId = $this->getBlockIdAt($x, $y, $z);
$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 = $this->getChunk($x >> 4, $z >> 4)->recalculateHeightMapColumn($x & 0x0f, $z & 0x0f);
}elseif($yPlusOne > $oldHeightMap){ //Block **placed** above the heightmap.
$this->setHeightMap($x, $z, $yPlusOne);
$newHeightMap = $yPlusOne;
}else{ //block changed below heightmap
$newHeightMap = $oldHeightMap;
}
$update = new SkyLightUpdate($this);
if($newHeightMap > $oldHeightMap){ //Heightmap increase, block placed, remove sky light
for($i = $y; $i >= $oldHeightMap; --$i){
$update->setAndUpdateLight($x, $i, $z, 0); //Remove all light beneath, adjacent recalculation will handle the rest.
}
}elseif($newHeightMap < $oldHeightMap){ //Heightmap decrease, block changed or removed, add sky light
for($i = $y; $i >= $newHeightMap; --$i){
$update->setAndUpdateLight($x, $i, $z, 15);
}
}else{ //No heightmap change, block changed "underground"
$update->setAndUpdateLight($x, $y, $z, max(0, $this->getHighestAdjacentBlockSkyLight($x, $y, $z) - Block::$lightFilter[$sourceId]));
}
$update->execute();
$this->timings->doBlockSkyLightUpdates->stopTiming();
}
/**
* Returns the highest light level available in the positions adjacent to the specified block coordinates.
* Returns the highest block light level available in the positions adjacent to the specified block coordinates.
*
* @param int $x
* @param int $y
@ -1337,98 +1387,16 @@ class Level implements ChunkManager, Metadatable{
public function updateBlockLight(int $x, int $y, int $z){
$this->timings->doBlockLightUpdates->startTiming();
$lightPropagationQueue = new \SplQueue();
$lightRemovalQueue = new \SplQueue();
$visited = [];
$removalVisited = [];
$id = $this->getBlockIdAt($x, $y, $z);
$oldLevel = $this->getBlockLightAt($x, $y, $z);
$newLevel = max(Block::$light[$id], $this->getHighestAdjacentBlockLight($x, $y, $z) - Block::$lightFilter[$id]);
if($oldLevel !== $newLevel){
$this->setBlockLightAt($x, $y, $z, $newLevel);
if($newLevel < $oldLevel){
$removalVisited[Level::blockHash($x, $y, $z)] = true;
$lightRemovalQueue->enqueue([new Vector3($x, $y, $z), $oldLevel]);
}else{
$visited[Level::blockHash($x, $y, $z)] = true;
$lightPropagationQueue->enqueue(new Vector3($x, $y, $z));
}
}
while(!$lightRemovalQueue->isEmpty()){
/** @var Vector3 $node */
$val = $lightRemovalQueue->dequeue();
$node = $val[0];
$lightLevel = $val[1];
$this->computeRemoveBlockLight($node->x - 1, $node->y, $node->z, $lightLevel, $lightRemovalQueue, $lightPropagationQueue, $removalVisited, $visited);
$this->computeRemoveBlockLight($node->x + 1, $node->y, $node->z, $lightLevel, $lightRemovalQueue, $lightPropagationQueue, $removalVisited, $visited);
$this->computeRemoveBlockLight($node->x, $node->y - 1, $node->z, $lightLevel, $lightRemovalQueue, $lightPropagationQueue, $removalVisited, $visited);
$this->computeRemoveBlockLight($node->x, $node->y + 1, $node->z, $lightLevel, $lightRemovalQueue, $lightPropagationQueue, $removalVisited, $visited);
$this->computeRemoveBlockLight($node->x, $node->y, $node->z - 1, $lightLevel, $lightRemovalQueue, $lightPropagationQueue, $removalVisited, $visited);
$this->computeRemoveBlockLight($node->x, $node->y, $node->z + 1, $lightLevel, $lightRemovalQueue, $lightPropagationQueue, $removalVisited, $visited);
}
while(!$lightPropagationQueue->isEmpty()){
/** @var Vector3 $node */
$node = $lightPropagationQueue->dequeue();
$lightLevel = $this->getBlockLightAt($node->x, $node->y, $node->z);
if($lightLevel >= 1){
$this->computeSpreadBlockLight($node->x - 1, $node->y, $node->z, $lightLevel, $lightPropagationQueue, $visited);
$this->computeSpreadBlockLight($node->x + 1, $node->y, $node->z, $lightLevel, $lightPropagationQueue, $visited);
$this->computeSpreadBlockLight($node->x, $node->y - 1, $node->z, $lightLevel, $lightPropagationQueue, $visited);
$this->computeSpreadBlockLight($node->x, $node->y + 1, $node->z, $lightLevel, $lightPropagationQueue, $visited);
$this->computeSpreadBlockLight($node->x, $node->y, $node->z - 1, $lightLevel, $lightPropagationQueue, $visited);
$this->computeSpreadBlockLight($node->x, $node->y, $node->z + 1, $lightLevel, $lightPropagationQueue, $visited);
}
}
$update = new BlockLightUpdate($this);
$update->setAndUpdateLight($x, $y, $z, $newLevel);
$update->execute();
$this->timings->doBlockLightUpdates->stopTiming();
}
private function computeRemoveBlockLight(int $x, int $y, int $z, int $currentLight, \SplQueue $queue, \SplQueue $spreadQueue, array &$visited, array &$spreadVisited){
if($y < 0) return;
$current = $this->getBlockLightAt($x, $y, $z);
if($current !== 0 and $current < $currentLight){
$this->setBlockLightAt($x, $y, $z, 0);
if(!isset($visited[$index = Level::blockHash($x, $y, $z)])){
$visited[$index] = true;
if($current > 1){
$queue->enqueue([new Vector3($x, $y, $z), $current]);
}
}
}elseif($current >= $currentLight){
if(!isset($spreadVisited[$index = Level::blockHash($x, $y, $z)])){
$spreadVisited[$index] = true;
$spreadQueue->enqueue(new Vector3($x, $y, $z));
}
}
}
private function computeSpreadBlockLight(int $x, int $y, int $z, int $currentLight, \SplQueue $queue, array &$visited){
if($y < 0) return;
$current = $this->getBlockLightAt($x, $y, $z);
$currentLight -= Block::$lightFilter[$this->getBlockIdAt($x, $y, $z)];
if($current < $currentLight){
$this->setBlockLightAt($x, $y, $z, $currentLight);
if(!isset($visited[$index = Level::blockHash($x, $y, $z)])){
$visited[$index] = true;
if($currentLight > 1){
$queue->enqueue(new Vector3($x, $y, $z));
}
}
}
}
/**
* Sets on Vector3 the data from a Block object,
* does block updates and puts the changes to the send queue.
@ -2179,6 +2147,29 @@ class Level implements ChunkManager, Metadatable{
return null;
}
/**
* Returns the chunks adjacent to the specified chunk.
*
* @param int $x
* @param int $z
*
* @return Chunk[]
*/
public function getAdjacentChunks(int $x, int $z) : array{
$result = [];
for($xx = 0; $xx <= 2; ++$xx){
for($zz = 0; $zz <= 2; ++$zz){
$i = $zz * 3 + $xx;
if($i === 4){
continue; //center chunk
}
$result[$i] = $this->getChunk($x + $xx - 1, $z + $zz - 1, false);
}
}
return $result;
}
public function generateChunkCallback(int $x, int $z, Chunk $chunk){
Timings::$generationCallbackTimer->startTiming();
if(isset($this->chunkPopulationQueue[$index = Level::chunkHash($x, $z)])){

View File

@ -0,0 +1,161 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
namespace pocketmine\level;
use pocketmine\block\Block;
//TODO: make light updates asynchronous
abstract class LightUpdate{
/** @var Level */
protected $level;
/** @var \SplQueue */
protected $spreadQueue;
/** @var bool[] */
protected $spreadVisited = [];
/** @var \SplQueue */
protected $removalQueue;
/** @var bool[] */
protected $removalVisited = [];
public function __construct(Level $level){
$this->level = $level;
$this->removalQueue = new \SplQueue();
$this->spreadQueue = new \SplQueue();
}
public function addSpreadNode(int $x, int $y, int $z){
$this->spreadQueue->enqueue([$x, $y, $z]);
}
public function addRemoveNode(int $x, int $y, int $z, int $oldLight){
$this->spreadQueue->enqueue([$x, $y, $z, $oldLight]);
}
abstract protected function getLight(int $x, int $y, int $z) : int;
abstract protected function setLight(int $x, int $y, int $z, int $level);
public function setAndUpdateLight(int $x, int $y, int $z, int $newLevel){
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){
$this->setLight($x, $y, $z, $newLevel);
if($oldLevel < $newLevel){ //light increased
$this->spreadVisited[$index] = true;
$this->spreadQueue->enqueue([$x, $y, $z]);
}else{ //light removed
$this->removalVisited[$index] = true;
$this->removalQueue->enqueue([$x, $y, $z, $oldLevel]);
}
}
}
public function execute(){
while(!$this->removalQueue->isEmpty()){
list($x, $y, $z, $oldAdjacentLight) = $this->removalQueue->dequeue();
$points = [
[$x + 1, $y, $z],
[$x - 1, $y, $z],
[$x, $y + 1, $z],
[$x, $y - 1, $z],
[$x, $y, $z + 1],
[$x, $y, $z - 1]
];
foreach($points as list($cx, $cy, $cz)){
if($cy < 0){
continue;
}
$this->computeRemoveLight($cx, $cy, $cz, $oldAdjacentLight);
}
}
while(!$this->spreadQueue->isEmpty()){
list($x, $y, $z) = $this->spreadQueue->dequeue();
$newAdjacentLight = $this->getLight($x, $y, $z);
if($newAdjacentLight <= 0){
continue;
}
$points = [
[$x + 1, $y, $z],
[$x - 1, $y, $z],
[$x, $y + 1, $z],
[$x, $y - 1, $z],
[$x, $y, $z + 1],
[$x, $y, $z - 1]
];
foreach($points as list($cx, $cy, $cz)){
if($cy < 0){
continue;
}
$this->computeSpreadLight($cx, $cy, $cz, $newAdjacentLight);
}
}
}
protected function computeRemoveLight(int $x, int $y, int $z, int $oldAdjacentLevel){
$current = $this->getLight($x, $y, $z);
if($current !== 0 and $current < $oldAdjacentLevel){
$this->setLight($x, $y, $z, 0);
if(!isset($visited[$index = Level::blockHash($x, $y, $z)])){
$this->removalVisited[$index] = true;
if($current > 1){
$this->removalQueue->enqueue([$x, $y, $z, $current]);
}
}
}elseif($current >= $oldAdjacentLevel){
if(!isset($this->spreadVisited[$index = Level::blockHash($x, $y, $z)])){
$this->spreadVisited[$index] = true;
$this->spreadQueue->enqueue([$x, $y, $z]);
}
}
}
protected function computeSpreadLight(int $x, int $y, int $z, int $newAdjacentLevel){
$current = $this->getLight($x, $y, $z);
$potentialLight = $newAdjacentLevel - Block::$lightFilter[$this->level->getBlockIdAt($x, $y, $z)];
if($current < $potentialLight){
$this->setLight($x, $y, $z, $potentialLight);
if(!isset($this->spreadVisited[$index = Level::blockHash($x, $y, $z)])){
$this->spreadVisited[$index] = true;
if($potentialLight > 1){
$this->spreadQueue->enqueue([$x, $y, $z]);
}
}
}
}
}

View File

@ -0,0 +1,34 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
namespace pocketmine\level;
class SkyLightUpdate extends LightUpdate{
public function getLight(int $x, int $y, int $z) : int{
return $this->level->getBlockSkyLightAt($x, $y, $z);
}
public function setLight(int $x, int $y, int $z, int $level){
$this->level->setBlockSkyLightAt($x, $y, $z, $level);
}
}

View File

@ -119,7 +119,7 @@ class Chunk{
$this->heightMap = $heightMap;
}else{
assert(count($heightMap) === 0, "Wrong HeightMap value count, expected 256, got " . count($heightMap));
$val = ($this->height * 16) - 1;
$val = ($this->height * 16);
$this->heightMap = array_fill(0, 256, $val);
}
@ -344,22 +344,13 @@ class Chunk{
*
* @param int $x 0-15
* @param int $z 0-15
* @param bool $useHeightMap whether to use pre-calculated heightmap values or not
*
* @return int
* @return int 0-255, or -1 if there are no blocks in the column
*/
public function getHighestBlockAt(int $x, int $z, bool $useHeightMap = true) : int{
if($useHeightMap){
$height = $this->getHeightMap($x, $z);
if($height !== 0 and $height !== 255){
return $height;
}
}
public function getHighestBlockAt(int $x, int $z) : int{
$index = $this->getHighestSubChunkIndex();
if($index < 0){
return 0;
if($index === -1){
return -1;
}
$height = $index << 4;
@ -367,12 +358,11 @@ class Chunk{
for($y = $index; $y >= 0; --$y){
$height = $this->getSubChunk($y)->getHighestBlockAt($x, $z) | ($y << 4);
if($height !== -1){
break;
return $height;
}
}
$this->setHeightMap($x, $z, $height);
return $height;
return -1;
}
/**
@ -403,15 +393,37 @@ class Chunk{
public function recalculateHeightMap(){
for($z = 0; $z < 16; ++$z){
for($x = 0; $x < 16; ++$x){
$this->setHeightMap($x, $z, $this->getHighestBlockAt($x, $z, false));
$this->recalculateHeightMapColumn($x, $z);
}
}
}
/**
* Performs basic sky light population on the chunk.
* Recalculates the heightmap for the block column at the specified X/Z chunk coordinates
*
* TODO: rewrite this, use block light filters and diffusion, actual proper sky light population
* @param int $x 0-15
* @param int $z 0-15
*
* @return int New calculated heightmap value (0-256 inclusive)
*/
public function recalculateHeightMapColumn(int $x, int $z) : int{
$max = $this->getHighestBlockAt($x, $z);
for($y = $max; $y >= 0; --$y){
if(Block::$lightFilter[$id = $this->getBlockId($x, $y, $z)] > 1 or Block::$diffusesSkyLight[$id]){
break;
}
}
$this->setHeightMap($x, $z, $y + 1);
return $y + 1;
}
/**
* Performs basic sky light population on the chunk.
* This does not cater for adjacent sky light, this performs direct sky light population only. This may cause some strange visual artifacts
* if the chunk is light-populated after being terrain-populated.
*
* TODO: fast adjacent light spread
*/
public function populateSkyLight(){
for($x = 0; $x < 16; ++$x){
@ -420,19 +432,19 @@ class Chunk{
$y = ($this->getHighestSubChunkIndex() + 1) << 4;
//TODO: replace a section of the array with a string in one call to improve performance
for(; $y > $heightMap; --$y){
for(; $y >= $heightMap; --$y){
$this->setBlockSkyLight($x, $y, $z, 15);
}
for(; $y > 0 and $this->getBlockId($x, $y, $z) === Block::AIR; --$y){
$this->setBlockSkyLight($x, $y, $z, 15);
}
$this->setHeightMap($x, $z, $y);
$light = 15;
for(; $y > 0; --$y){
$this->setBlockSkyLight($x, $y, $z, 0);
if($light > 0){
$light -= Block::$lightFilter[$this->getBlockId($x, $y, $z)];
if($light < 0){
$light = 0;
}
}
$this->setBlockSkyLight($x, $y, $z, $light);
}
}
}
@ -920,9 +932,9 @@ class Chunk{
}
$stream->putByte($count);
$stream->put($subChunks);
$stream->put(pack("C*", ...$this->heightMap) .
$stream->put(pack("v*", ...$this->heightMap) .
$this->biomeIds .
chr(($this->lightPopulated ? 1 << 2 : 0) | ($this->terrainPopulated ? 1 << 1 : 0) | ($this->terrainGenerated ? 1 : 0)));
chr(($this->lightPopulated ? 4 : 0) | ($this->terrainPopulated ? 2 : 0) | ($this->terrainGenerated ? 1 : 0)));
return $stream->getBuffer();
}
@ -944,7 +956,7 @@ class Chunk{
for($y = 0; $y < $count; ++$y){
$subChunks[$stream->getByte()] = SubChunk::fastDeserialize($stream->get(10240));
}
$heightMap = array_values(unpack("C*", $stream->get(256)));
$heightMap = array_values(unpack("v*", $stream->get(512)));
$biomeIds = $stream->get(256);
$chunk = new Chunk($x, $z, $subChunks, [], [], $biomeIds, $heightMap);

View File

@ -159,9 +159,11 @@ class SubChunk{
}
public function getHighestBlockAt(int $x, int $z) : int{
for($y = 15; $y >= 0; --$y){
if($this->ids{($x << 8) | ($z << 4) | $y} !== "\x00"){
return $y;
$low = ($x << 8) | ($z << 4);
$i = $low | 0x0f;
for(; $i >= $low; --$i){
if($this->ids{$i} !== "\x00"){
return $i & 0x0f;
}
}

View File

@ -48,14 +48,8 @@ class PopulationTask extends AsyncTask{
$this->levelId = $level->getId();
$this->chunk = $chunk->fastSerialize();
for($i = 0; $i < 9; ++$i){
if($i === 4){
continue;
}
$xx = -1 + $i % 3;
$zz = -1 + (int) ($i / 3);
$ck = $level->getChunk($chunk->getX() + $xx, $chunk->getZ() + $zz, false);
$this->{"chunk$i"} = $ck !== null ? $ck->fastSerialize() : null;
foreach($level->getAdjacentChunks($chunk->getX(), $chunk->getZ()) as $i => $c){
$this->{"chunk$i"} = $c !== null ? $c->fastSerialize() : null;
}
}
@ -173,4 +167,4 @@ class PopulationTask extends AsyncTask{
$level->generateChunkCallback($chunk->getX(), $chunk->getZ(), $chunk);
}
}
}
}