Updated Levels :D

This commit is contained in:
Shoghi Cervantes
2014-06-09 11:35:52 +02:00
parent 920e2a7c7e
commit 115b4cf4ac
41 changed files with 1492 additions and 270 deletions

View File

@ -22,20 +22,122 @@
namespace pocketmine\level\format\anvil;
use pocketmine\level\format\generic\BaseLevelProvider;
use pocketmine\level\Level;
use pocketmine\Player;
class Anvil extends BaseLevelProvider{
protected $basePath;
public function __construct($path, $levelName){
$this->basePath = realpath($path) . "/";
}
/** @var RegionLoader */
protected $regions = [];
/** @var Chunk[] */
protected $chunks = [];
public static function isValid($path){
return file_exists(realpath($path) . "region/");
return file_exists(realpath($path) . "level.dat") and file_exists(realpath($path) . "region/");
}
public static function getRegionIndex($chunkX, $chunkZ, &$x, &$z){
$x = $chunkX >> 5;
$z = $chunkZ >> 5;
}
public function unloadChunks(){
$this->chunks = [];
}
public function getLoadedChunks(){
return $this->chunks;
}
public function isChunkLoaded($x, $z){
return isset($this->chunks[Level::chunkHash($x, $z)]);
}
public function saveChunks(){
foreach($this->chunks as $chunk){
$this->saveChunk($chunk->getX(), $chunk->getZ());
}
}
public function loadChunk($chunkX, $chunkZ){
$index = Level::chunkHash($chunkX, $chunkZ);
if(isset($this->chunks[$index])){
return true;
}
$regionX = $regionZ = null;
self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ);
$this->loadRegion($regionX, $regionZ);
$chunk = $this->getRegion($regionX, $regionZ)->readChunk($chunkX - $regionX * 32, $chunkZ - $regionZ * 32, true); //generate empty chunk if not loaded
if($chunk instanceof Chunk){
$this->chunks[$index] = $chunk;
}else{
return false;
}
}
public function unloadChunk($x, $z, $safe = true){
if($safe === true and $this->isChunkLoaded($x, $z)){
$chunk = $this->getChunk($x, $z);
foreach($chunk->getEntities() as $entity){
if($entity instanceof Player){
return false;
}
}
}
unset($this->chunks[Level::chunkHash($x, $z)]);
return true;
}
public function saveChunk($x, $z){
if($this->isChunkLoaded($x, $z)){
$this->getRegion($x >> 5, $z >> 5)->writeChunk($this->getChunk($x, $z));
return true;
}
return false;
}
/**
* @param $x
* @param $z
*
* @return RegionLoader
*/
protected function getRegion($x, $z){
$index = $x.":".$z;
return isset($this->regions[$index]) ? $this->regions[$index] : null;
}
public function getChunk($chunkX, $chunkZ, $create = false){
$index = Level::chunkHash($chunkX, $chunkZ);
if(isset($this->chunks[$index])){
return $this->chunks[$index];
}elseif($create !== true){
return null;
}
$this->loadChunk($chunkX, $chunkZ);
return $this->getChunk($chunkX, $chunkZ, false);
}
public function isChunkGenerated($chunkX, $chunkZ){
if(($region = $this->getRegion($chunkX >> 5, $chunkZ >> 5)) instanceof RegionLoader){
return $region->chunkExists($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32);
}
return false;
}
protected function loadRegion($x, $z){
$index = $x.":".$z;
if(isset($this->regions[$index])){
return true;
}
$this->regions[$index] = new RegionLoader($this, $x, $z);
return true;
}
}

View File

@ -23,6 +23,7 @@ namespace pocketmine\level\format\anvil;
use pocketmine\level\format\generic\BaseChunk;
use pocketmine\level\format\generic\EmptyChunkSection;
use pocketmine\level\format\LevelProvider;
use pocketmine\level\Level;
use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\Compound;
@ -33,7 +34,7 @@ class Chunk extends BaseChunk{
/** @var Compound */
protected $nbt;
public function __construct(Level $level, Compound $nbt){
public function __construct(LevelProvider $level, Compound $nbt){
$this->nbt = $nbt;
if($this->nbt->Entities instanceof Enum){
@ -71,7 +72,7 @@ class Chunk extends BaseChunk{
}
}
parent::__construct($level, $this->nbt["xPos"], $this->nbt["zPos"], $sections);
parent::__construct($level, $this->nbt["xPos"], $this->nbt["zPos"], $sections, $this->nbt["Entities"], $this->nbt["TileEntities"]);
}
public function getChunkSnapshot($includeMaxBlockY = true, $includeBiome = false, $includeBiomeTemp = false){
@ -101,6 +102,14 @@ class Chunk extends BaseChunk{
//TODO: maxBlockY, biomeMap, biomeTemp
return new ChunkSnapshot($this->getX(), $this->getZ(), $this->getLevel()->getName(), $this->getLevel()->getTime(), $blockId, $blockData, $blockSkyLight, $blockLight, $emptySections, null, null, null, null);
//TODO: time
return new ChunkSnapshot($this->getX(), $this->getZ(), $this->getLevel()->getName(), 0/*$this->getLevel()->getTime()*/, $blockId, $blockData, $blockSkyLight, $blockLight, $emptySections, null, null, null, null);
}
/**
* @return Compound
*/
public function getNBT(){
return $this->nbt;
}
}

View File

@ -39,6 +39,10 @@ class ChunkSection implements \pocketmine\level\format\ChunkSection{
$this->skyLight = (string) $nbt["SkyLight"];
}
public function getY(){
return $this->y;
}
public function getBlockId($x, $y, $z){
return ord($this->blocks{($y << 8) + ($z << 4) + $x});
}

View File

@ -21,6 +21,7 @@
namespace pocketmine\level\format\anvil;
use pocketmine\level\format\LevelProvider;
use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\Byte;
use pocketmine\nbt\tag\ByteArray;
@ -29,6 +30,7 @@ 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;
class RegionLoader{
@ -42,14 +44,15 @@ class RegionLoader{
protected $filePath;
protected $filePointer;
protected $lastSector;
/** @var LevelProvider */
protected $levelProvider;
protected $locationTable = [];
public function __construct($path, /*Level $level, */
$regionX, $regionZ){
public function __construct(LevelProvider $level, $regionX, $regionZ){
$this->x = $regionX;
$this->z = $regionZ;
$this->filePath = /*$level->getPath()*/
$path . "region/r.$regionX.$regionZ.mca";
$this->levelProvider = $level;
$this->filePath = $this->levelProvider->getPath() . "region/r.$regionX.$regionZ.mca";
touch($this->filePath);
$this->filePointer = fopen($this->filePath, "r+b");
flock($this->filePointer, LOCK_EX);
@ -71,13 +74,17 @@ class RegionLoader{
}
}
protected function isChunkGenerated($index){
return !($this->locationTable[$index][0] === 0 or $this->locationTable[$index][1] === 0);
}
public function readChunk($x, $z, $generate = true){
$index = self::getChunkOffset($x, $z);
if($index < 0 or $index >= 4096){
return false;
}
if($this->locationTable[$index][0] === 0 or $this->locationTable[$index][1] === 0){
if(!$this->isChunkGenerated($index)){
if($generate === true){
//Allocate space
$this->locationTable[$index][0] = ++$this->lastSector;
@ -116,8 +123,11 @@ class RegionLoader{
return false;
}
return $chunk;
//$chunk = new Chunk($level, $chunk);
return new Chunk($this->levelProvider, $chunk);
}
public function chunkExists($x, $z){
return $this->isChunkGenerated(self::getChunkOffset($x, $z));
}
public function generateChunk($x, $z){
@ -131,6 +141,8 @@ class RegionLoader{
$nbt->InhabitedTime = new Long("InhabitedTime", 0);
$nbt->Biomes = new ByteArray("Biomes", str_repeat(Binary::writeByte(-1), 256));
$nbt->HeightMap = new IntArray("HeightMap", array_fill(0, 256, 127));
//TODO: check type and name
//$nbt->GrassMap = new IntArray("GrassMap", array_fill(0, 256, 127));
$nbt->Sections = new Enum("Sections", []);
$nbt->Sections->setTagType(NBT::TAG_Compound);
$nbt->Entities = new Enum("Entities", []);
@ -139,6 +151,10 @@ class RegionLoader{
$nbt->TileEntities->setTagType(NBT::TAG_Compound);
$nbt->TileTicks = new Enum("TileTicks", []);
$nbt->TileTicks->setTagType(NBT::TAG_Compound);
$this->saveChunk($x, $z, $nbt);
}
protected function saveChunk($x, $z, Compound $nbt){
$writer = new NBT(NBT::BIG_ENDIAN);
$writer->setData(new Compound("", array($nbt)));
$chunkData = $writer->writeCompressed(self::COMPRESSION_ZLIB, self::$COMPRESSION_LEVEL);
@ -154,6 +170,47 @@ class RegionLoader{
fwrite($this->filePointer, str_pad(Binary::writeInt($length) . chr(self::COMPRESSION_ZLIB) . $chunkData, $sectors << 12, "\x00", STR_PAD_RIGHT));
}
public function writeChunk(Chunk $chunk){
$nbt = $chunk->getNBT();
$nbt->Sections = new Enum("Sections", []);
$nbt->Sections->setTagType(NBT::TAG_Compound);
foreach($chunk->getSections() as $section){
$nbt->Sections[$section->getY()] = new Compound(null, [
"Y" => new Byte("Y", $section->getY()),
"Blocks" => new ByteArray("Blocks", $section->getIdArray()),
"Data" => new ByteArray("Data", $section->getDataArray()),
"BlockLight" => new ByteArray("BlockLight", $section->getLightArray()),
"SkyLight" => new ByteArray("SkyLight", $section->getSkyLightArray())
]);
}
$entities = [];
foreach($chunk->getEntities() as $entity){
if(!($entity instanceof Player) and $entity->closed !== true){
$entity->saveNBT();
$entities[] = $entity->namedtag;
}
}
$nbt->Entities = new Enum("Entities", $entities);
$nbt->Entities->setTagType(NBT::TAG_Compound);
$tiles = [];
foreach($chunk->getTiles() as $tile){
if($tile->closed !== true){
$tile->saveNBT();
$tiles[] = $tile->namedtag;
}
}
$nbt->Entities = new Enum("TileEntities", $tiles);
$nbt->Entities->setTagType(NBT::TAG_Compound);
$this->saveChunk($chunk->getX() - ($this->getX() * 32), $chunk->getZ() - ($this->getZ() * 32), $nbt);
}
protected static function getChunkOffset($x, $z){
return $x + ($z << 5);
}