mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-08 10:53:05 +00:00
World generation is timed by type and chunks can be created on the fly
This commit is contained in:
@ -346,4 +346,13 @@ interface FullChunk{
|
||||
*/
|
||||
public static function fromFastBinary($data, LevelProvider $provider = null);
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param LevelProvider $provider
|
||||
*
|
||||
* @return FullChunk
|
||||
*/
|
||||
public static function getEmptyChunk($chunkX, $chunkZ, LevelProvider $provider = null);
|
||||
|
||||
}
|
@ -105,6 +105,10 @@ class Anvil extends McRegion{
|
||||
$this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $chunk;
|
||||
}
|
||||
|
||||
public function getEmptyChunk($chunkX, $chunkZ){
|
||||
return Chunk::getEmptyChunk($chunkX, $chunkZ, $this);
|
||||
}
|
||||
|
||||
public static function createChunkSection($Y){
|
||||
return new ChunkSection(new Compound("", [
|
||||
"Y" => new Byte("Y", $Y),
|
||||
|
@ -40,7 +40,11 @@ class Chunk extends BaseChunk{
|
||||
protected $nbt;
|
||||
|
||||
public function __construct($level, Compound $nbt){
|
||||
$this->nbt = $nbt;
|
||||
if($nbt === null){
|
||||
$this->provider = $level;
|
||||
$this->nbt = new Compound("Level", []);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!isset($this->nbt->Entities) or !($this->nbt->Entities instanceof Enum)){
|
||||
$this->nbt->Entities = new Enum("Entities", []);
|
||||
@ -63,11 +67,11 @@ class Chunk extends BaseChunk{
|
||||
}
|
||||
|
||||
if(!isset($this->nbt->BiomeColors) or !($this->nbt->BiomeColors instanceof IntArray)){
|
||||
$this->nbt->BiomeColors = new IntArray("BiomeColors", array_fill(0, 256, Binary::readInt("\x00\x85\xb2\x4a")));
|
||||
$this->nbt->BiomeColors = new IntArray("BiomeColors", array_fill(0, 256, 0));
|
||||
}
|
||||
|
||||
if(!isset($this->nbt->HeightMap) or !($this->nbt->HeightMap instanceof IntArray)){
|
||||
$this->nbt->HeightMap = new IntArray("HeightMap", array_fill(0, 256, 127));
|
||||
$this->nbt->HeightMap = new IntArray("HeightMap", array_fill(0, 256, 0));
|
||||
}
|
||||
|
||||
$sections = [];
|
||||
@ -287,4 +291,32 @@ class Chunk extends BaseChunk{
|
||||
|
||||
return $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param LevelProvider $provider
|
||||
*
|
||||
* @return Chunk
|
||||
*/
|
||||
public static function getEmptyChunk($chunkX, $chunkZ, LevelProvider $provider = null){
|
||||
try{
|
||||
$chunk = new Chunk($provider instanceof LevelProvider ? $provider : Anvil::class, null);
|
||||
$chunk->x = $chunkX;
|
||||
$chunk->z = $chunkZ;
|
||||
|
||||
$chunk->heightMap = array_fill(0, 256, 0);
|
||||
$chunk->biomeColors = array_fill(0, 256, 0);
|
||||
|
||||
$chunk->nbt->V = new Byte("V", 1);
|
||||
$chunk->nbt->InhabitedTime = new Long("InhabitedTime", 0);
|
||||
$chunk->nbt->TerrainGenerated = new Byte("TerrainGenerated", 0);
|
||||
$chunk->nbt->TerrainPopulated = new Byte("TerrainPopulated", 0);
|
||||
$chunk->nbt->LightPopulated = new Byte("LightPopulated", 0);
|
||||
|
||||
return $chunk;
|
||||
}catch(\Exception $e){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -57,33 +57,4 @@ class RegionLoader extends \pocketmine\level\format\mcregion\RegionLoader{
|
||||
protected function unserializeChunk($data){
|
||||
return Chunk::fromBinary($data, $this->levelProvider);
|
||||
}
|
||||
|
||||
public function generateChunk($x, $z){
|
||||
$nbt = new Compound("Level", []);
|
||||
$nbt->xPos = new Int("xPos", ($this->getX() * 32) + $x);
|
||||
$nbt->zPos = new Int("zPos", ($this->getZ() * 32) + $z);
|
||||
$nbt->LastUpdate = new Long("LastUpdate", 0);
|
||||
$nbt->LightPopulated = new Byte("LightPopulated", 0);
|
||||
$nbt->TerrainPopulated = new Byte("TerrainPopulated", 0);
|
||||
$nbt->V = new Byte("V", self::VERSION);
|
||||
$nbt->InhabitedTime = new Long("InhabitedTime", 0);
|
||||
$biomes = str_repeat(Binary::writeByte(-1), 256);
|
||||
$nbt->Biomes = new ByteArray("Biomes", $biomes);
|
||||
$nbt->BiomeColors = new IntArray("BiomeColors", array_fill(0, 156, Binary::readInt("\x00\x85\xb2\x4a")));
|
||||
$nbt->HeightMap = new IntArray("HeightMap", array_fill(0, 256, 127));
|
||||
$nbt->Sections = new Enum("Sections", []);
|
||||
$nbt->Sections->setTagType(NBT::TAG_Compound);
|
||||
$nbt->Entities = new Enum("Entities", []);
|
||||
$nbt->Entities->setTagType(NBT::TAG_Compound);
|
||||
$nbt->TileEntities = new Enum("TileEntities", []);
|
||||
$nbt->TileEntities->setTagType(NBT::TAG_Compound);
|
||||
$nbt->TileTicks = new Enum("TileTicks", []);
|
||||
$nbt->TileTicks->setTagType(NBT::TAG_Compound);
|
||||
$writer = new NBT(NBT::BIG_ENDIAN);
|
||||
$nbt->setName("Level");
|
||||
$writer->setData(new Compound("", ["Level" => $nbt]));
|
||||
$chunkData = $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL);
|
||||
$this->saveChunk($x, $z, $chunkData);
|
||||
}
|
||||
|
||||
}
|
@ -339,4 +339,19 @@ class Chunk extends BaseFullChunk{
|
||||
($this->isLightPopulated() ? 0x04 : 0) | ($this->isPopulated() ? 0x02 : 0) | ($this->isGenerated() ? 0x01 : 0)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param LevelProvider $provider
|
||||
*
|
||||
* @return Chunk
|
||||
*/
|
||||
public static function getEmptyChunk($chunkX, $chunkZ, LevelProvider $provider = null){
|
||||
try{
|
||||
return new Chunk($provider instanceof LevelProvider ? $provider : LevelDB::class, $chunkX, $chunkZ, str_repeat("\x00", 16384 * (2 + 1 + 1 + 1) + 256 + 1024));
|
||||
}catch(\Exception $e){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -197,10 +197,13 @@ class LevelDB extends BaseLevelProvider{
|
||||
}
|
||||
|
||||
$this->level->timings->syncChunkLoadDataTimer->startTiming();
|
||||
$chunk = $this->readChunk($chunkX, $chunkZ, $create); //generate empty chunk if not loaded
|
||||
$chunk = $this->readChunk($chunkX, $chunkZ, $create);
|
||||
if($chunk === null and $create){
|
||||
$chunk = Chunk::getEmptyChunk($chunkX, $chunkZ, $this);
|
||||
}
|
||||
$this->level->timings->syncChunkLoadDataTimer->stopTiming();
|
||||
|
||||
if($chunk instanceof Chunk){
|
||||
if($chunk !== null){
|
||||
$this->chunks[$index] = $chunk;
|
||||
return true;
|
||||
}else{
|
||||
@ -211,15 +214,14 @@ class LevelDB extends BaseLevelProvider{
|
||||
/**
|
||||
* @param $chunkX
|
||||
* @param $chunkZ
|
||||
* @param bool $create
|
||||
*
|
||||
* @return Chunk
|
||||
*/
|
||||
private function readChunk($chunkX, $chunkZ, $create = false){
|
||||
private function readChunk($chunkX, $chunkZ){
|
||||
$index = LevelDB::chunkIndex($chunkX, $chunkZ);
|
||||
|
||||
if(!$this->chunkExists($chunkX, $chunkZ) or ($data = $this->db->get($index . "\x30")) === false){
|
||||
return $create ? $this->generateChunk($chunkX, $chunkZ) : null;
|
||||
return null;
|
||||
}
|
||||
|
||||
$flags = $this->db->get($index . "f");
|
||||
|
@ -30,6 +30,7 @@ use pocketmine\nbt\tag\Compound;
|
||||
use pocketmine\nbt\tag\Enum;
|
||||
use pocketmine\nbt\tag\Int;
|
||||
use pocketmine\nbt\tag\IntArray;
|
||||
use pocketmine\nbt\tag\Long;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\utils\Binary;
|
||||
|
||||
@ -40,6 +41,7 @@ class Chunk extends BaseFullChunk{
|
||||
|
||||
public function __construct($level, Compound $nbt = null){
|
||||
if($nbt === null){
|
||||
$this->provider = $level;
|
||||
$this->nbt = new Compound("Level", []);
|
||||
return;
|
||||
}
|
||||
@ -68,11 +70,11 @@ class Chunk extends BaseFullChunk{
|
||||
}
|
||||
|
||||
if(!isset($this->nbt->BiomeColors) or !($this->nbt->BiomeColors instanceof IntArray)){
|
||||
$this->nbt->BiomeColors = new IntArray("BiomeColors", array_fill(0, 256, Binary::readInt("\x01\x85\xb2\x4a")));
|
||||
$this->nbt->BiomeColors = new IntArray("BiomeColors", array_fill(0, 256, 0));
|
||||
}
|
||||
|
||||
if(!isset($this->nbt->HeightMap) or !($this->nbt->HeightMap instanceof IntArray)){
|
||||
$this->nbt->HeightMap = new IntArray("HeightMap", array_fill(0, 256, 127));
|
||||
$this->nbt->HeightMap = new IntArray("HeightMap", array_fill(0, 256, 0));
|
||||
}
|
||||
|
||||
if(!isset($this->nbt->Blocks)){
|
||||
@ -410,4 +412,37 @@ class Chunk extends BaseFullChunk{
|
||||
public function getNBT(){
|
||||
return $this->nbt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param LevelProvider $provider
|
||||
*
|
||||
* @return Chunk
|
||||
*/
|
||||
public static function getEmptyChunk($chunkX, $chunkZ, LevelProvider $provider = null){
|
||||
try{
|
||||
$chunk = new Chunk($provider instanceof LevelProvider ? $provider : McRegion::class, null);
|
||||
$chunk->x = $chunkX;
|
||||
$chunk->z = $chunkZ;
|
||||
|
||||
$chunk->data = str_repeat("\x00", 16384);
|
||||
$chunk->blocks = $chunk->data . $chunk->data;
|
||||
$chunk->skyLight = $chunk->data;
|
||||
$chunk->blockLight = $chunk->data;
|
||||
|
||||
$chunk->heightMap = array_fill(0, 256, 0);
|
||||
$chunk->biomeColors = array_fill(0, 256, 0);
|
||||
|
||||
$chunk->nbt->V = new Byte("V", 1);
|
||||
$chunk->nbt->InhabitedTime = new Long("InhabitedTime", 0);
|
||||
$chunk->nbt->TerrainGenerated = new Byte("TerrainGenerated", 0);
|
||||
$chunk->nbt->TerrainPopulated = new Byte("TerrainPopulated", 0);
|
||||
$chunk->nbt->LightPopulated = new Byte("LightPopulated", 0);
|
||||
|
||||
return $chunk;
|
||||
}catch(\Exception $e){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -192,10 +192,13 @@ class McRegion extends BaseLevelProvider{
|
||||
self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ);
|
||||
$this->loadRegion($regionX, $regionZ);
|
||||
$this->level->timings->syncChunkLoadDataTimer->startTiming();
|
||||
$chunk = $this->getRegion($regionX, $regionZ)->readChunk($chunkX - $regionX * 32, $chunkZ - $regionZ * 32, $create); //generate empty chunk if not loaded
|
||||
$chunk = $this->getRegion($regionX, $regionZ)->readChunk($chunkX - $regionX * 32, $chunkZ - $regionZ * 32);
|
||||
if($chunk === null and $create){
|
||||
$chunk = $this->getEmptyChunk($chunkX, $chunkZ);
|
||||
}
|
||||
$this->level->timings->syncChunkLoadDataTimer->stopTiming();
|
||||
|
||||
if($chunk instanceof FullChunk){
|
||||
if($chunk !== null){
|
||||
$this->chunks[$index] = $chunk;
|
||||
return true;
|
||||
}else{
|
||||
@ -203,6 +206,10 @@ class McRegion extends BaseLevelProvider{
|
||||
}
|
||||
}
|
||||
|
||||
public function getEmptyChunk($chunkX, $chunkZ){
|
||||
return Chunk::getEmptyChunk($chunkX, $chunkZ, $this);
|
||||
}
|
||||
|
||||
public function unloadChunk($x, $z, $safe = true){
|
||||
$chunk = isset($this->chunks[$index = Level::chunkHash($x, $z)]) ? $this->chunks[$index] : null;
|
||||
if($chunk instanceof FullChunk and $chunk->unload(false, $safe)){
|
||||
|
@ -83,7 +83,7 @@ class RegionLoader{
|
||||
return !($this->locationTable[$index][0] === 0 or $this->locationTable[$index][1] === 0);
|
||||
}
|
||||
|
||||
public function readChunk($x, $z, $generate = true, $forward = false){
|
||||
public function readChunk($x, $z){
|
||||
$index = self::getChunkOffset($x, $z);
|
||||
if($index < 0 or $index >= 4096){
|
||||
return null;
|
||||
@ -92,16 +92,7 @@ class RegionLoader{
|
||||
$this->lastUsed = time();
|
||||
|
||||
if(!$this->isChunkGenerated($index)){
|
||||
if($generate === true){
|
||||
//Allocate space
|
||||
$this->locationTable[$index][0] = ++$this->lastSector;
|
||||
$this->locationTable[$index][1] = 1;
|
||||
fseek($this->filePointer, $this->locationTable[$index][0] << 12);
|
||||
fwrite($this->filePointer, str_pad(Binary::writeInt(0) . chr(self::COMPRESSION_ZLIB), 4096, "\x00", STR_PAD_RIGHT));
|
||||
$this->writeLocationIndex($index);
|
||||
}else{
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
fseek($this->filePointer, $this->locationTable[$index][0] << 12);
|
||||
@ -114,10 +105,7 @@ class RegionLoader{
|
||||
$this->locationTable[$index][1] = 1;
|
||||
MainLogger::getLogger()->error("Corrupted chunk header detected");
|
||||
}
|
||||
$this->generateChunk($x, $z);
|
||||
fseek($this->filePointer, $this->locationTable[$index][0] << 12);
|
||||
$length = Binary::readInt(fread($this->filePointer, 4));
|
||||
$compression = ord(fgetc($this->filePointer));
|
||||
return null;
|
||||
}
|
||||
|
||||
if($length > ($this->locationTable[$index][1] << 12)){ //Invalid chunk, bigger than defined number of sectors
|
||||
@ -126,19 +114,14 @@ class RegionLoader{
|
||||
$this->writeLocationIndex($index);
|
||||
}elseif($compression !== self::COMPRESSION_ZLIB and $compression !== self::COMPRESSION_GZIP){
|
||||
MainLogger::getLogger()->error("Invalid compression type");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$chunk = $this->unserializeChunk(fread($this->filePointer, $length - 1));
|
||||
if($chunk instanceof FullChunk){
|
||||
return $chunk;
|
||||
}elseif($forward === false){
|
||||
MainLogger::getLogger()->error("Corrupted chunk detected");
|
||||
$this->generateChunk($x, $z);
|
||||
|
||||
return $this->readChunk($x, $z, $generate, true);
|
||||
}else{
|
||||
MainLogger::getLogger()->error("Corrupted chunk detected");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -151,43 +134,6 @@ class RegionLoader{
|
||||
return $this->isChunkGenerated(self::getChunkOffset($x, $z));
|
||||
}
|
||||
|
||||
public function generateChunk($x, $z){
|
||||
$nbt = new Compound("Level", []);
|
||||
$nbt->xPos = new Int("xPos", ($this->getX() * 32) + $x);
|
||||
$nbt->zPos = new Int("zPos", ($this->getZ() * 32) + $z);
|
||||
$nbt->LastUpdate = new Long("LastUpdate", 0);
|
||||
$nbt->LightPopulated = new Byte("LightPopulated", 0);
|
||||
$nbt->TerrainPopulated = new Byte("TerrainPopulated", 0);
|
||||
$nbt->V = new Byte("V", self::VERSION);
|
||||
$nbt->InhabitedTime = new Long("InhabitedTime", 0);
|
||||
$biomes = str_repeat(Binary::writeByte(-1), 256);
|
||||
$nbt->Biomes = new ByteArray("Biomes", $biomes);
|
||||
$nbt->HeightMap = new IntArray("HeightMap", array_fill(0, 256, 127));
|
||||
$nbt->BiomeColors = new IntArray("BiomeColors", array_fill(0, 256, Binary::readInt("\x00\x85\xb2\x4a")));
|
||||
|
||||
$half = str_repeat("\x00", 16384);
|
||||
$full = $half . $half;
|
||||
$nbt->Blocks = new ByteArray("Blocks", $full);
|
||||
$nbt->Data = new ByteArray("Data", $half);
|
||||
$nbt->SkyLight = new ByteArray("SkyLight", str_repeat("\xff", 16384));
|
||||
$nbt->BlockLight = new ByteArray("BlockLight", $half);
|
||||
|
||||
$nbt->Entities = new Enum("Entities", []);
|
||||
$nbt->Entities->setTagType(NBT::TAG_Compound);
|
||||
$nbt->TileEntities = new Enum("TileEntities", []);
|
||||
$nbt->TileEntities->setTagType(NBT::TAG_Compound);
|
||||
$nbt->TileTicks = new Enum("TileTicks", []);
|
||||
$nbt->TileTicks->setTagType(NBT::TAG_Compound);
|
||||
$writer = new NBT(NBT::BIG_ENDIAN);
|
||||
$nbt->setName("Level");
|
||||
$writer->setData(new Compound("", ["Level" => $nbt]));
|
||||
$chunkData = $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, self::$COMPRESSION_LEVEL);
|
||||
|
||||
if($chunkData !== false){
|
||||
$this->saveChunk($x, $z, $chunkData);
|
||||
}
|
||||
}
|
||||
|
||||
protected function saveChunk($x, $z, $chunkData){
|
||||
$length = strlen($chunkData) + 1;
|
||||
if($length + 4 > self::MAX_SECTOR_LENGTH){
|
||||
|
Reference in New Issue
Block a user