World generation is timed by type and chunks can be created on the fly

This commit is contained in:
Shoghi Cervantes 2015-05-09 19:03:41 +02:00
parent ab18b7833f
commit db409851e9
No known key found for this signature in database
GPG Key ID: 78464DB0A7837F89
14 changed files with 212 additions and 177 deletions

View File

@ -664,7 +664,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
++$count; ++$count;
if(!$this->level->populateChunk($X, $Z)){ if(!$this->level->populateChunk($X, $Z)){
if($this->teleportPosition === null){ if($this->spawned and $this->teleportPosition === null){
continue; continue;
}else{ }else{
break; break;
@ -1391,10 +1391,8 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
$this->timings->startTiming(); $this->timings->startTiming();
if($this->spawned){ if($this->spawned){
$this->processMovement($tickDiff); $this->processMovement($tickDiff);
$this->entityBaseTick($tickDiff); $this->entityBaseTick($tickDiff);
if(!$this->isSpectator()){ if(!$this->isSpectator()){
@ -1575,8 +1573,6 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
if($p->kick("logged in from another location") === false){ if($p->kick("logged in from another location") === false){
$this->close($this->getLeaveMessage(), "Logged in from another location"); $this->close($this->getLeaveMessage(), "Logged in from another location");
return;
}else{
return; return;
} }
} }

View File

@ -1228,8 +1228,6 @@ class Server{
asort($order); asort($order);
$chunkX = $chunkZ = null;
foreach($order as $index => $distance){ foreach($order as $index => $distance){
Level::getXZ($index, $chunkX, $chunkZ); Level::getXZ($index, $chunkX, $chunkZ);
$level->generateChunk($chunkX, $chunkZ, true); $level->generateChunk($chunkX, $chunkZ, true);

View File

@ -55,6 +55,10 @@ abstract class Timings{
/** @var TimingsHandler */ /** @var TimingsHandler */
public static $generationTimer; public static $generationTimer;
/** @var TimingsHandler */ /** @var TimingsHandler */
public static $populationTimer;
/** @var TimingsHandler */
public static $generationCallbackTimer;
/** @var TimingsHandler */
public static $permissibleCalculationTimer; public static $permissibleCalculationTimer;
/** @var TimingsHandler */ /** @var TimingsHandler */
public static $permissionDefaultTimer; public static $permissionDefaultTimer;
@ -81,8 +85,6 @@ abstract class Timings{
/** @var TimingsHandler */ /** @var TimingsHandler */
public static $timerEntityTickRest; public static $timerEntityTickRest;
/** @var TimingsHandler */
public static $processQueueTimer;
/** @var TimingsHandler */ /** @var TimingsHandler */
public static $schedulerSyncTimer; public static $schedulerSyncTimer;
/** @var TimingsHandler */ /** @var TimingsHandler */
@ -115,6 +117,8 @@ abstract class Timings{
self::$serverCommandTimer = new TimingsHandler("Server Command"); self::$serverCommandTimer = new TimingsHandler("Server Command");
self::$worldSaveTimer = new TimingsHandler("World Save"); self::$worldSaveTimer = new TimingsHandler("World Save");
self::$generationTimer = new TimingsHandler("World Generation"); self::$generationTimer = new TimingsHandler("World Generation");
self::$populationTimer = new TimingsHandler("World Population");
self::$generationCallbackTimer = new TimingsHandler("World Generation Callback");
self::$permissibleCalculationTimer = new TimingsHandler("Permissible Calculation"); self::$permissibleCalculationTimer = new TimingsHandler("Permissible Calculation");
self::$permissionDefaultTimer = new TimingsHandler("Default Permission Calculation"); self::$permissionDefaultTimer = new TimingsHandler("Default Permission Calculation");
@ -130,7 +134,6 @@ abstract class Timings{
self::$timerEntityAIMove = new TimingsHandler("** livingEntityAIMove"); self::$timerEntityAIMove = new TimingsHandler("** livingEntityAIMove");
self::$timerEntityTickRest = new TimingsHandler("** livingEntityTickRest"); self::$timerEntityTickRest = new TimingsHandler("** livingEntityTickRest");
self::$processQueueTimer = new TimingsHandler("processQueue");
self::$schedulerSyncTimer = new TimingsHandler("** Scheduler - Sync Tasks", PluginManager::$pluginParentTimer); self::$schedulerSyncTimer = new TimingsHandler("** Scheduler - Sync Tasks", PluginManager::$pluginParentTimer);
self::$schedulerAsyncTimer = new TimingsHandler("** Scheduler - Async Tasks"); self::$schedulerAsyncTimer = new TimingsHandler("** Scheduler - Async Tasks");

View File

@ -612,16 +612,16 @@ class Level implements ChunkManager, Metadatable{
$this->timings->entityTick->stopTiming(); $this->timings->entityTick->stopTiming();
$this->timings->tileEntityTick->startTiming(); $this->timings->tileEntityTick->startTiming();
Timings::$tickTileEntityTimer->startTiming();
//Update tiles that need update //Update tiles that need update
if(count($this->updateTiles) > 0){ if(count($this->updateTiles) > 0){
//Timings::$tickTileEntityTimer->startTiming();
foreach($this->updateTiles as $id => $tile){ foreach($this->updateTiles as $id => $tile){
if($tile->onUpdate() !== true){ if($tile->onUpdate() !== true){
unset($this->updateTiles[$id]); unset($this->updateTiles[$id]);
} }
} }
//Timings::$tickTileEntityTimer->stopTiming();
} }
Timings::$tickTileEntityTimer->stopTiming();
$this->timings->tileEntityTick->stopTiming(); $this->timings->tileEntityTick->stopTiming();
$this->timings->doTickTiles->startTiming(); $this->timings->doTickTiles->startTiming();
@ -1897,7 +1897,7 @@ class Level implements ChunkManager, Metadatable{
} }
public function generateChunkCallback($x, $z, FullChunk $chunk){ public function generateChunkCallback($x, $z, FullChunk $chunk){
Timings::$generationTimer->startTiming(); Timings::$generationCallbackTimer->startTiming();
if(isset($this->chunkPopulationQueue[$index = Level::chunkHash($x, $z)])){ if(isset($this->chunkPopulationQueue[$index = Level::chunkHash($x, $z)])){
$oldChunk = $this->getChunk($x, $z, false); $oldChunk = $this->getChunk($x, $z, false);
for($xx = -1; $xx <= 1; ++$xx){ for($xx = -1; $xx <= 1; ++$xx){
@ -1918,7 +1918,7 @@ class Level implements ChunkManager, Metadatable{
$chunk->setProvider($this->provider); $chunk->setProvider($this->provider);
$this->setChunk($x, $z, $chunk); $this->setChunk($x, $z, $chunk);
} }
Timings::$generationTimer->stopTiming(); Timings::$generationCallbackTimer->stopTiming();
} }
public function setChunk($x, $z, FullChunk $chunk = null, $unload = true){ public function setChunk($x, $z, FullChunk $chunk = null, $unload = true){
@ -2150,6 +2150,8 @@ class Level implements ChunkManager, Metadatable{
return true; return true;
} }
$this->timings->syncChunkLoadTimer->startTiming();
$this->cancelUnloadChunkRequest($x, $z); $this->cancelUnloadChunkRequest($x, $z);
$chunk = $this->provider->getChunk($x, $z, $generate); $chunk = $this->provider->getChunk($x, $z, $generate);
@ -2157,14 +2159,13 @@ class Level implements ChunkManager, Metadatable{
$this->chunks[$index] = $chunk; $this->chunks[$index] = $chunk;
$chunk->initChunk(); $chunk->initChunk();
}else{ }else{
$this->timings->syncChunkLoadTimer->startTiming();
$this->provider->loadChunk($x, $z, $generate); $this->provider->loadChunk($x, $z, $generate);
$this->timings->syncChunkLoadTimer->stopTiming();
if(($chunk = $this->provider->getChunk($x, $z)) !== null){ if(($chunk = $this->provider->getChunk($x, $z)) !== null){
$this->chunks[$index] = $chunk; $this->chunks[$index] = $chunk;
$chunk->initChunk(); $chunk->initChunk();
}else{ }else{
$this->timings->syncChunkLoadTimer->stopTiming();
return false; return false;
} }
} }
@ -2173,11 +2174,14 @@ class Level implements ChunkManager, Metadatable{
$this->server->getPluginManager()->callEvent(new ChunkLoadEvent($chunk, !$chunk->isGenerated())); $this->server->getPluginManager()->callEvent(new ChunkLoadEvent($chunk, !$chunk->isGenerated()));
}else{ }else{
$this->unloadChunk($x, $z, false); $this->unloadChunk($x, $z, false);
$this->timings->syncChunkLoadTimer->stopTiming();
return false; return false;
} }
$chunk->setChanged(false); $chunk->setChanged(false);
$this->timings->syncChunkLoadTimer->stopTiming();
return true; return true;
} }
@ -2408,16 +2412,15 @@ class Level implements ChunkManager, Metadatable{
return false; return false;
} }
Timings::$generationTimer->startTiming(); Timings::$populationTimer->startTiming();
if(!$this->getChunk($x, $z, true)->isPopulated()){ $chunk = $this->getChunk($x, $z, false);
if($chunk === null or !$chunk->isPopulated()){
$populate = true; $populate = true;
for($xx = -1; $xx <= 1; ++$xx){ for($xx = -1; $xx <= 1; ++$xx){
for($zz = -1; $zz <= 1; ++$zz){ for($zz = -1; $zz <= 1; ++$zz){
if(isset($this->chunkPopulationLock[Level::chunkHash($x + $xx, $z + $zz)])){ if(isset($this->chunkPopulationLock[Level::chunkHash($x + $xx, $z + $zz)])){
$populate = false; $populate = false;
}elseif(!$this->getChunk($x + $xx, $z + $zz, true)->isGenerated()){ break;
$populate = false;
$this->generateChunk($x + $xx, $z + $zz, $force);
} }
} }
} }
@ -2430,17 +2433,17 @@ class Level implements ChunkManager, Metadatable{
$this->chunkPopulationLock[Level::chunkHash($x + $xx, $z + $zz)] = true; $this->chunkPopulationLock[Level::chunkHash($x + $xx, $z + $zz)] = true;
} }
} }
$task = new PopulationTask($this, $this->getChunk($x, $z, true)); $task = new PopulationTask($this, $x, $z, $chunk);
$this->server->getScheduler()->scheduleAsyncTask($task); $this->server->getScheduler()->scheduleAsyncTask($task);
} }
Timings::$generationTimer->stopTiming(); Timings::$populationTimer->stopTiming();
return false; return false;
} }
Timings::$generationTimer->stopTiming(); Timings::$populationTimer->stopTiming();
return false; return false;
} }
Timings::$generationTimer->stopTiming(); Timings::$populationTimer->stopTiming();
return true; return true;
} }

View File

@ -346,4 +346,13 @@ interface FullChunk{
*/ */
public static function fromFastBinary($data, LevelProvider $provider = null); 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);
} }

View File

@ -105,6 +105,10 @@ class Anvil extends McRegion{
$this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $chunk; $this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $chunk;
} }
public function getEmptyChunk($chunkX, $chunkZ){
return Chunk::getEmptyChunk($chunkX, $chunkZ, $this);
}
public static function createChunkSection($Y){ public static function createChunkSection($Y){
return new ChunkSection(new Compound("", [ return new ChunkSection(new Compound("", [
"Y" => new Byte("Y", $Y), "Y" => new Byte("Y", $Y),

View File

@ -40,7 +40,11 @@ class Chunk extends BaseChunk{
protected $nbt; protected $nbt;
public function __construct($level, Compound $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)){ if(!isset($this->nbt->Entities) or !($this->nbt->Entities instanceof Enum)){
$this->nbt->Entities = new Enum("Entities", []); $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)){ 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)){ 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 = []; $sections = [];
@ -287,4 +291,32 @@ class Chunk extends BaseChunk{
return $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL); 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;
}
}
} }

View File

@ -57,33 +57,4 @@ class RegionLoader extends \pocketmine\level\format\mcregion\RegionLoader{
protected function unserializeChunk($data){ protected function unserializeChunk($data){
return Chunk::fromBinary($data, $this->levelProvider); 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);
}
} }

View File

@ -339,4 +339,19 @@ class Chunk extends BaseFullChunk{
($this->isLightPopulated() ? 0x04 : 0) | ($this->isPopulated() ? 0x02 : 0) | ($this->isGenerated() ? 0x01 : 0) ($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;
}
}
} }

View File

@ -197,10 +197,13 @@ class LevelDB extends BaseLevelProvider{
} }
$this->level->timings->syncChunkLoadDataTimer->startTiming(); $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(); $this->level->timings->syncChunkLoadDataTimer->stopTiming();
if($chunk instanceof Chunk){ if($chunk !== null){
$this->chunks[$index] = $chunk; $this->chunks[$index] = $chunk;
return true; return true;
}else{ }else{
@ -211,15 +214,14 @@ class LevelDB extends BaseLevelProvider{
/** /**
* @param $chunkX * @param $chunkX
* @param $chunkZ * @param $chunkZ
* @param bool $create
* *
* @return Chunk * @return Chunk
*/ */
private function readChunk($chunkX, $chunkZ, $create = false){ private function readChunk($chunkX, $chunkZ){
$index = LevelDB::chunkIndex($chunkX, $chunkZ); $index = LevelDB::chunkIndex($chunkX, $chunkZ);
if(!$this->chunkExists($chunkX, $chunkZ) or ($data = $this->db->get($index . "\x30")) === false){ 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"); $flags = $this->db->get($index . "f");

View File

@ -30,6 +30,7 @@ use pocketmine\nbt\tag\Compound;
use pocketmine\nbt\tag\Enum; use pocketmine\nbt\tag\Enum;
use pocketmine\nbt\tag\Int; use pocketmine\nbt\tag\Int;
use pocketmine\nbt\tag\IntArray; use pocketmine\nbt\tag\IntArray;
use pocketmine\nbt\tag\Long;
use pocketmine\Player; use pocketmine\Player;
use pocketmine\utils\Binary; use pocketmine\utils\Binary;
@ -40,6 +41,7 @@ class Chunk extends BaseFullChunk{
public function __construct($level, Compound $nbt = null){ public function __construct($level, Compound $nbt = null){
if($nbt === null){ if($nbt === null){
$this->provider = $level;
$this->nbt = new Compound("Level", []); $this->nbt = new Compound("Level", []);
return; return;
} }
@ -68,11 +70,11 @@ class Chunk extends BaseFullChunk{
} }
if(!isset($this->nbt->BiomeColors) or !($this->nbt->BiomeColors instanceof IntArray)){ 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)){ 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)){ if(!isset($this->nbt->Blocks)){
@ -410,4 +412,37 @@ class Chunk extends BaseFullChunk{
public function getNBT(){ public function getNBT(){
return $this->nbt; 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;
}
}
} }

View File

@ -192,10 +192,13 @@ class McRegion extends BaseLevelProvider{
self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ); self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ);
$this->loadRegion($regionX, $regionZ); $this->loadRegion($regionX, $regionZ);
$this->level->timings->syncChunkLoadDataTimer->startTiming(); $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(); $this->level->timings->syncChunkLoadDataTimer->stopTiming();
if($chunk instanceof FullChunk){ if($chunk !== null){
$this->chunks[$index] = $chunk; $this->chunks[$index] = $chunk;
return true; return true;
}else{ }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){ public function unloadChunk($x, $z, $safe = true){
$chunk = isset($this->chunks[$index = Level::chunkHash($x, $z)]) ? $this->chunks[$index] : null; $chunk = isset($this->chunks[$index = Level::chunkHash($x, $z)]) ? $this->chunks[$index] : null;
if($chunk instanceof FullChunk and $chunk->unload(false, $safe)){ if($chunk instanceof FullChunk and $chunk->unload(false, $safe)){

View File

@ -83,7 +83,7 @@ class RegionLoader{
return !($this->locationTable[$index][0] === 0 or $this->locationTable[$index][1] === 0); 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); $index = self::getChunkOffset($x, $z);
if($index < 0 or $index >= 4096){ if($index < 0 or $index >= 4096){
return null; return null;
@ -92,17 +92,8 @@ class RegionLoader{
$this->lastUsed = time(); $this->lastUsed = time();
if(!$this->isChunkGenerated($index)){ 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); fseek($this->filePointer, $this->locationTable[$index][0] << 12);
$length = Binary::readInt(fread($this->filePointer, 4)); $length = Binary::readInt(fread($this->filePointer, 4));
@ -114,10 +105,7 @@ class RegionLoader{
$this->locationTable[$index][1] = 1; $this->locationTable[$index][1] = 1;
MainLogger::getLogger()->error("Corrupted chunk header detected"); MainLogger::getLogger()->error("Corrupted chunk header detected");
} }
$this->generateChunk($x, $z); return null;
fseek($this->filePointer, $this->locationTable[$index][0] << 12);
$length = Binary::readInt(fread($this->filePointer, 4));
$compression = ord(fgetc($this->filePointer));
} }
if($length > ($this->locationTable[$index][1] << 12)){ //Invalid chunk, bigger than defined number of sectors if($length > ($this->locationTable[$index][1] << 12)){ //Invalid chunk, bigger than defined number of sectors
@ -126,19 +114,14 @@ class RegionLoader{
$this->writeLocationIndex($index); $this->writeLocationIndex($index);
}elseif($compression !== self::COMPRESSION_ZLIB and $compression !== self::COMPRESSION_GZIP){ }elseif($compression !== self::COMPRESSION_ZLIB and $compression !== self::COMPRESSION_GZIP){
MainLogger::getLogger()->error("Invalid compression type"); MainLogger::getLogger()->error("Invalid compression type");
return null; return null;
} }
$chunk = $this->unserializeChunk(fread($this->filePointer, $length - 1)); $chunk = $this->unserializeChunk(fread($this->filePointer, $length - 1));
if($chunk instanceof FullChunk){ if($chunk instanceof FullChunk){
return $chunk; return $chunk;
}elseif($forward === false){
MainLogger::getLogger()->error("Corrupted chunk detected");
$this->generateChunk($x, $z);
return $this->readChunk($x, $z, $generate, true);
}else{ }else{
MainLogger::getLogger()->error("Corrupted chunk detected");
return null; return null;
} }
} }
@ -151,43 +134,6 @@ class RegionLoader{
return $this->isChunkGenerated(self::getChunkOffset($x, $z)); 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){ protected function saveChunk($x, $z, $chunkData){
$length = strlen($chunkData) + 1; $length = strlen($chunkData) + 1;
if($length + 4 > self::MAX_SECTOR_LENGTH){ if($length + 4 > self::MAX_SECTOR_LENGTH){

View File

@ -36,33 +36,37 @@ class PopulationTask extends AsyncTask{
public $state; public $state;
public $levelId; public $levelId;
public $chunk; public $chunk;
public $chunkX;
public $chunkZ;
public $chunkClass; public $chunkClass;
public $chunk00; public $chunk0;
public $chunk01; public $chunk1;
public $chunk02; public $chunk2;
public $chunk10; public $chunk3;
//center chunk //center chunk
public $chunk12; public $chunk5;
public $chunk20; public $chunk6;
public $chunk21; public $chunk7;
public $chunk22; public $chunk8;
public function __construct(Level $level, FullChunk $chunk){ public function __construct(Level $level, $chunkX, $chunkZ, FullChunk $chunk = null){
$this->state = true; $this->state = true;
$this->levelId = $level->getId(); $this->levelId = $level->getId();
$this->chunk = $chunk->toFastBinary(); $this->chunk = $chunk !== null ? $chunk->toFastBinary() : null;
$this->chunkX = $chunkX;
$this->chunkZ = $chunkZ;
$this->chunkClass = get_class($chunk); $this->chunkClass = get_class($chunk);
$this->chunk00 = $level->getChunk($chunk->getX() - 1, $chunk->getZ() - 1, true)->toFastBinary(); for($i = 0; $i < 9; ++$i){
$this->chunk01 = $level->getChunk($chunk->getX() - 1, $chunk->getZ(), true)->toFastBinary(); if($i === 4){
$this->chunk02 = $level->getChunk($chunk->getX() - 1, $chunk->getZ() + 1, true)->toFastBinary(); continue;
$this->chunk10 = $level->getChunk($chunk->getX(), $chunk->getZ() - 1, true)->toFastBinary(); }
$xx = -1 + $i % 3;
$this->chunk12 = $level->getChunk($chunk->getX(), $chunk->getZ() + 1, true)->toFastBinary(); $zz = -1 + (int) ($i / 3);
$this->chunk20 = $level->getChunk($chunk->getX() + 1, $chunk->getZ() - 1, true)->toFastBinary(); $ck = $level->getChunk($chunkX + $xx, $chunkZ + $zz, false);
$this->chunk21 = $level->getChunk($chunk->getX() + 1, $chunk->getZ(), true)->toFastBinary(); $this->{"chunk$i"} = $ck !== null ? $ck->toFastBinary() : null;
$this->chunk22 = $level->getChunk($chunk->getX() + 1, $chunk->getZ() + 1, true)->toFastBinary(); }
} }
public function onRun(){ public function onRun(){
@ -75,20 +79,29 @@ class PopulationTask extends AsyncTask{
return; return;
} }
$chunkX = $this->chunkX;
$chunkZ = $this->chunkZ;
/** @var FullChunk[] $chunks */ /** @var FullChunk[] $chunks */
$chunks = []; $chunks = [];
/** @var FullChunk $chunkC */ /** @var FullChunk $chunkC */
$chunkC = $this->chunkClass; $chunkC = $this->chunkClass;
$chunks[0] = $chunkC::fromFastBinary($this->chunk00); $chunk = $this->chunk !== null ? $chunkC::fromFastBinary($this->chunk) : $chunkC::getEmptyChunk($chunkX, $chunkZ);
$chunks[1] = $chunkC::fromFastBinary($this->chunk01);
$chunks[2] = $chunkC::fromFastBinary($this->chunk02); for($i = 0; $i < 9; ++$i){
$chunks[3] = $chunkC::fromFastBinary($this->chunk10); if($i === 4){
$chunk = $chunkC::fromFastBinary($this->chunk); continue;
$chunks[5] = $chunkC::fromFastBinary($this->chunk12); }
$chunks[6] = $chunkC::fromFastBinary($this->chunk20); $xx = -1 + $i % 3;
$chunks[7] = $chunkC::fromFastBinary($this->chunk21); $zz = -1 + (int) ($i / 3);
$chunks[8] = $chunkC::fromFastBinary($this->chunk22); $ck = $this->{"chunk$i"};
if($ck === null){
$chunks[$i] = $chunkC::getEmptyChunk($chunkX + $xx, $chunkZ + $zz);
}else{
$chunks[$i] = $chunkC::fromFastBinary($ck);
}
}
if($chunk === null){ if($chunk === null){
//TODO error //TODO error
@ -96,10 +109,18 @@ class PopulationTask extends AsyncTask{
} }
$manager->setChunk($chunk->getX(), $chunk->getZ(), $chunk); $manager->setChunk($chunk->getX(), $chunk->getZ(), $chunk);
if(!$chunk->isGenerated()){
$generator->generateChunk($chunk->getX(), $chunk->getZ());
$chunk->setGenerated();
}
foreach($chunks as $c){ foreach($chunks as $c){
if($c !== null){ if($c !== null){
$manager->setChunk($c->getX(), $c->getZ(), $c); $manager->setChunk($c->getX(), $c->getZ(), $c);
if(!$c->isGenerated()){
$generator->generateChunk($c->getX(), $c->getZ());
$c->setGenerated();
}
} }
} }
@ -128,15 +149,13 @@ class PopulationTask extends AsyncTask{
$manager->cleanChunks(); $manager->cleanChunks();
$this->chunk00 = $chunks[0] !== null ? $chunks[0]->toFastBinary() : null; for($i = 0; $i < 9; ++$i){
$this->chunk01 = $chunks[1] !== null ? $chunks[1]->toFastBinary() : null; if($i === 4){
$this->chunk02 = $chunks[2] !== null ? $chunks[2]->toFastBinary() : null; continue;
$this->chunk10 = $chunks[3] !== null ? $chunks[3]->toFastBinary() : null; }
$this->chunk12 = $chunks[5] !== null ? $chunks[5]->toFastBinary() : null; $this->{"chunk$i"} = $chunks[$i] !== null ? $chunks[$i]->toFastBinary() : null;
$this->chunk20 = $chunks[6] !== null ? $chunks[6]->toFastBinary() : null; }
$this->chunk21 = $chunks[7] !== null ? $chunks[7]->toFastBinary() : null;
$this->chunk22 = $chunks[8] !== null ? $chunks[8]->toFastBinary() : null;
} }
public function onCompletion(Server $server){ public function onCompletion(Server $server){
@ -146,33 +165,28 @@ class PopulationTask extends AsyncTask{
$level->registerGenerator(); $level->registerGenerator();
return; return;
} }
/** @var FullChunk[] $chunks */
$chunks = [];
/** @var FullChunk $chunkC */ /** @var FullChunk $chunkC */
$chunkC = $this->chunkClass; $chunkC = $this->chunkClass;
$chunks[0] = $chunkC::fromFastBinary($this->chunk00, $level->getProvider());
$chunks[1] = $chunkC::fromFastBinary($this->chunk01, $level->getProvider());
$chunks[2] = $chunkC::fromFastBinary($this->chunk02, $level->getProvider());
$chunks[3] = $chunkC::fromFastBinary($this->chunk10, $level->getProvider());
$chunk = $chunkC::fromFastBinary($this->chunk, $level->getProvider()); $chunk = $chunkC::fromFastBinary($this->chunk, $level->getProvider());
$chunks[5] = $chunkC::fromFastBinary($this->chunk12, $level->getProvider());
$chunks[6] = $chunkC::fromFastBinary($this->chunk20, $level->getProvider());
$chunks[7] = $chunkC::fromFastBinary($this->chunk21, $level->getProvider());
$chunks[8] = $chunkC::fromFastBinary($this->chunk22, $level->getProvider());
foreach($chunks as $c){
if($c !== null){
$level->generateChunkCallback($c->getX(), $c->getZ(), $c);
}
}
if($chunk === null){ if($chunk === null){
//TODO error //TODO error
return; return;
} }
for($i = 0; $i < 9; ++$i){
if($i === 4){
continue;
}
$c = $this->{"chunk$i"};
if($c !== null){
$c = $chunkC::fromFastBinary($c);
$level->generateChunkCallback($c->getX(), $c->getZ(), $c);
}
}
$level->generateChunkCallback($chunk->getX(), $chunk->getZ(), $chunk); $level->generateChunkCallback($chunk->getX(), $chunk->getZ(), $chunk);
} }
} }