Reuse McRegion classes on Anvil

This commit is contained in:
Shoghi Cervantes 2014-07-30 16:38:56 +02:00
parent 016b08ecf2
commit 4128b22e85
3 changed files with 10 additions and 333 deletions

View File

@ -22,6 +22,7 @@
namespace pocketmine\level\format\anvil;
use pocketmine\level\format\generic\BaseLevelProvider;
use pocketmine\level\format\mcregion\McRegion;
use pocketmine\level\format\SimpleChunk;
use pocketmine\level\generator\Generator;
use pocketmine\level\Level;
@ -34,7 +35,7 @@ use pocketmine\nbt\tag\Long;
use pocketmine\nbt\tag\String;
use pocketmine\Player;
class Anvil extends BaseLevelProvider{
class Anvil extends McRegion{
/** @var RegionLoader[] */
protected $regions = [];
@ -69,68 +70,6 @@ class Anvil extends BaseLevelProvider{
return $isValid;
}
public static function generate($path, $name, $seed, $generator, array $options = []){
@mkdir($path, 0777, true);
@mkdir($path . "/region", 0777);
//TODO, add extra details
$levelData = new Compound("Data", [
"hardcore" => new Byte("hardcore", 0),
"initialized" => new Byte("initialized", 1),
"GameType" => new Int("GameType", 0),
"generatorVersion" => new Int("generatorVersion", 1), //2 in MCPE
"SpawnX" => new Int("SpawnX", 128),
"SpawnY" => new Int("SpawnY", 70),
"SpawnZ" => new Int("SpawnZ", 128),
"version" => new Int("version", 19133),
"DayTime" => new Int("DayTime", 0),
"LastPlayed" => new Long("LastPlayed", microtime(true) * 1000),
"RandomSeed" => new Long("RandomSeed", $seed),
"SizeOnDisk" => new Long("SizeOnDisk", 0),
"Time" => new Long("Time", 0),
"generatorName" => new String("generatorName", Generator::getGeneratorName($generator)),
"generatorOptions" => new String("generatorOptions", isset($options["preset"]) ? $options["preset"] : ""),
"LevelName" => new String("LevelName", $name),
"GameRules" => new Compound("GameRules", [])
]);
$nbt = new NBT(NBT::BIG_ENDIAN);
$nbt->setData(new Compound(null, [
"Data" => $levelData
]));
$buffer = $nbt->writeCompressed();
@file_put_contents($path . "level.dat", $buffer);
}
public static function getRegionIndex($chunkX, $chunkZ, &$x, &$z){
$x = $chunkX >> 5;
$z = $chunkZ >> 5;
}
public function unloadChunks(){
$this->chunks = [];
}
public function getGenerator(){
return $this->levelData["generatorName"];
}
public function getGeneratorOptions(){
return ["preset" => $this->levelData["generatorOptions"]];
}
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, $create = false){
$index = Level::chunkHash($chunkX, $chunkZ);
if(isset($this->chunks[$index])){
@ -151,54 +90,6 @@ class Anvil extends BaseLevelProvider{
}
}
public function unloadChunk($x, $z, $safe = true){
$chunk = $this->getChunk($x, $z, false);
if($chunk instanceof Chunk){
if($safe === true and $this->isChunkLoaded($x, $z)){
foreach($chunk->getEntities() as $entity){
if($entity instanceof Player){
return false;
}
}
}
foreach($chunk->getEntities() as $entity){
$entity->close();
}
foreach($chunk->getTiles() as $tile){
$tile->close();
}
$this->chunks[$index = Level::chunkHash($x, $z)] = null;
unset($this->chunks[$index]);
}
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;
}
/**
* @param int $chunkX
* @param int $chunkZ
@ -207,13 +98,7 @@ class Anvil extends BaseLevelProvider{
* @return Chunk
*/
public function getChunk($chunkX, $chunkZ, $create = false){
$index = Level::chunkHash($chunkX, $chunkZ);
if(isset($this->chunks[$index])){
return $this->chunks[$index];
}else{
$this->loadChunk($chunkX, $chunkZ, $create);
return isset($this->chunks[$index]) ? $this->chunks[$index] : null;
}
return parent::getChunk($chunkX, $chunkZ, $create);
}
public function setChunk($chunkX, $chunkZ, SimpleChunk $chunk){
@ -249,9 +134,9 @@ class Anvil extends BaseLevelProvider{
return new ChunkSection(new Compound(null, [
"Y" => new Byte("Y", $Y),
"Blocks" => new ByteArray("Blocks", str_repeat("\xff", 4096)),
"Data" => new ByteArray("Data", str_repeat("\xff", 2048)),
"SkyLight" => new ByteArray("SkyLight", str_repeat("\xff", 2048)), //TODO
"BlockLight" => new ByteArray("BlockLight", str_repeat("\x00", 2048)) //TODO
"Data" => new ByteArray("Data", $half = str_repeat("\xff", 2048)),
"SkyLight" => new ByteArray("SkyLight", $half),
"BlockLight" => new ByteArray("BlockLight", $half)
]));
}
@ -263,15 +148,6 @@ class Anvil extends BaseLevelProvider{
return false;
}
public function isChunkPopulated($chunkX, $chunkZ){
$chunk = $this->getChunk($chunkX, $chunkZ);
if($chunk instanceof Chunk){
return $chunk->isPopulated();
}else{
return false;
}
}
protected function loadRegion($x, $z){
$index = $x . ":" . $z;
if(isset($this->regions[$index])){
@ -282,12 +158,4 @@ class Anvil extends BaseLevelProvider{
return true;
}
public function close(){
$this->unloadChunks();
foreach($this->regions as $index => $region){
$region->close();
unset($this->regions[$index]);
}
}
}

View File

@ -33,21 +33,12 @@ use pocketmine\nbt\tag\Long;
use pocketmine\Player;
use pocketmine\utils\Binary;
class RegionLoader{
class RegionLoader extends \pocketmine\level\format\mcregion\RegionLoader{
const VERSION = 1;
const COMPRESSION_GZIP = 1;
const COMPRESSION_ZLIB = 2;
public static $COMPRESSION_LEVEL = 7;
protected $x;
protected $z;
protected $filePath;
protected $filePointer;
protected $lastSector;
/** @var LevelProvider */
protected $levelProvider;
protected $locationTable = [];
public function __construct(LevelProvider $level, $regionX, $regionZ){
$this->x = $regionX;
$this->z = $regionZ;
@ -65,19 +56,6 @@ class RegionLoader{
}
}
public function __destruct(){
if(is_resource($this->filePointer)){
$this->cleanGarbage();
$this->writeLocationTable();
flock($this->filePointer, LOCK_UN);
fclose($this->filePointer);
}
}
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){
@ -131,10 +109,6 @@ class RegionLoader{
return new Chunk($this->levelProvider, $chunk->Level);
}
public function chunkExists($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);
@ -158,30 +132,6 @@ class RegionLoader{
$this->saveChunk($x, $z, $nbt);
}
protected function saveChunk($x, $z, Compound $nbt){
$writer = new NBT(NBT::BIG_ENDIAN);
$nbt->setName("Level");
$writer->setData(new Compound("", array("Level" => $nbt)));
$chunkData = $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, self::$COMPRESSION_LEVEL);
$length = strlen($chunkData) + 1;
$sectors = (int) ceil(($length + 4) / 4096);
$index = self::getChunkOffset($x, $z);
if($this->locationTable[$index][1] < $sectors){
$this->locationTable[$index][0] = $this->lastSector += $sectors; //The GC will clean this shift later
}
$this->locationTable[$index][1] = $sectors;
fseek($this->filePointer, $this->locationTable[$index][0] << 12);
fwrite($this->filePointer, str_pad(Binary::writeInt($length) . chr(self::COMPRESSION_ZLIB) . $chunkData, $sectors << 12, "\x00", STR_PAD_RIGHT));
$this->writeLocationIndex($index);
}
public function removeChunk($x, $z){
$index = self::getChunkOffset($x, $z);
$this->locationTable[$index][0] = 0;
$this->locationTable[$index][1] = 0;
}
public function writeChunk(Chunk $chunk){
$nbt = $chunk->getNBT();
$nbt->Sections = new Enum("Sections", []);
@ -226,145 +176,4 @@ class RegionLoader{
$this->saveChunk($chunk->getX() - ($this->getX() * 32), $chunk->getZ() - ($this->getZ() * 32), $nbt);
}
protected static function getChunkOffset($x, $z){
return $x + ($z << 5);
}
public function close(){
$this->writeLocationTable();
flock($this->filePointer, LOCK_UN);
fclose($this->filePointer);
}
public function doSlowCleanUp(){
for($i = 0; $i < 1024; ++$i){
if($this->locationTable[$i][0] === 0 or $this->locationTable[$i][1] === 0){
continue;
}
fseek($this->filePointer, $this->locationTable[$i][0] << 12);
$chunk = fread($this->filePointer, $this->locationTable[$i][1] << 12);
$length = Binary::readInt(substr($chunk, 0, 4));
if($length <= 1){
$this->locationTable[$i] = array(0, 0, 0); //Non-generated chunk, remove it from index
}
$chunk = zlib_decode(substr($chunk, 5));
if(strlen($chunk) <= 1){
$this->locationTable[$i] = array(0, 0, 0); //Corrupted chunk, remove it
continue;
}
$chunk = chr(self::COMPRESSION_ZLIB) . zlib_encode($chunk, 15, 9);
$chunk = Binary::writeInt(strlen($chunk)) . $chunk;
$sectors = (int) ceil(strlen($chunk) / 4096);
if($sectors > $this->locationTable[$i][1]){
$this->locationTable[$i][0] = $this->lastSector += $sectors;
}
fseek($this->filePointer, $this->locationTable[$i][0] << 12);
fwrite($this->filePointer, str_pad($chunk, $sectors << 12, "\x00", STR_PAD_RIGHT));
}
$this->writeLocationTable();
$n = $this->cleanGarbage();
$this->writeLocationTable();
return $n;
}
private function cleanGarbage(){
$sectors = [];
foreach($this->locationTable as $index => $data){ //Calculate file usage
if($data[0] === 0 or $data[1] === 0){
$this->locationTable[$index] = array(0, 0, 0);
continue;
}
for($i = 0; $i < $data[1]; ++$i){
$sectors[$data[0]] = $index;
}
}
if(count($sectors) === ($this->lastSector - 2)){ //No collection needed
return 0;
}
ksort($sectors);
$shift = 0;
$lastSector = 1; //First chunk - 1
fseek($this->filePointer, 8192);
$sector = 2;
foreach($sectors as $sector => $index){
if(($sector - $lastSector) > 1){
$shift += $sector - $lastSector - 1;
}
if($shift > 0){
fseek($this->filePointer, $sector << 12);
$old = fread($this->filePointer, 4096);
fseek($this->filePointer, ($sector - $shift) << 12);
fwrite($this->filePointer, $old, 4096);
}
$this->locationTable[$index][0] -= $shift;
$lastSector = $sector;
}
ftruncate($this->filePointer, ($sector + 1) << 12); //Truncate to the end of file written
return $shift;
}
private function loadLocationTable(){
fseek($this->filePointer, 0);
$this->lastSector = 1;
$table = fread($this->filePointer, 4 * 1024 * 2);
for($i = 0; $i < 1024; ++$i){
$index = Binary::readInt(substr($table, $i << 2, 4));
$this->locationTable[$i] = array(($index & ~0xff) >> 8, $index & 0xff, Binary::readInt(substr($table, 4096 + ($i << 2), 4)));
if(($this->locationTable[$i][0] + $this->locationTable[$i][1] - 1) > $this->lastSector){
$this->lastSector = $this->locationTable[$i][0] + $this->locationTable[$i][1] - 1;
}
}
}
private function writeLocationTable(){
$table = "";
for($i = 0; $i < 1024; ++$i){
$table .= Binary::writeInt(($this->locationTable[$i][0] << 8) | $this->locationTable[$i][1]);
}
for($i = 0; $i < 1024; ++$i){
$table .= Binary::writeInt($this->locationTable[$i][2]);
}
fseek($this->filePointer, 0);
fwrite($this->filePointer, $table, 4096 * 2);
}
private function writeLocationIndex($index){
fseek($this->filePointer, $index << 2);
fwrite($this->filePointer, Binary::writeInt(($this->locationTable[$index][0] << 8) | $this->locationTable[$index][1]), 4);
fseek($this->filePointer, 4096 + ($index << 2));
fwrite($this->filePointer, Binary::writeInt($this->locationTable[$index][2]), 4);
}
private function createBlank(){
fseek($this->filePointer, 0);
ftruncate($this->filePointer, 0);
$this->lastSector = 1;
$table = "";
for($i = 0; $i < 1024; ++$i){
$this->locationTable[$i] = array(0, 0);
$table .= Binary::writeInt(0);
}
$time = time();
for($i = 0; $i < 1024; ++$i){
$this->locationTable[$i][2] = $time;
$table .= Binary::writeInt($time);
}
fwrite($this->filePointer, $table, 4096 * 2);
}
public function getX(){
return $this->x;
}
public function getZ(){
return $this->z;
}
}

View File

@ -305,7 +305,7 @@ class RegionLoader{
return $shift;
}
private function loadLocationTable(){
protected function loadLocationTable(){
fseek($this->filePointer, 0);
$this->lastSector = 1;
$table = fread($this->filePointer, 4 * 1024 * 2);
@ -331,14 +331,14 @@ class RegionLoader{
fwrite($this->filePointer, $table, 4096 * 2);
}
private function writeLocationIndex($index){
protected function writeLocationIndex($index){
fseek($this->filePointer, $index << 2);
fwrite($this->filePointer, Binary::writeInt(($this->locationTable[$index][0] << 8) | $this->locationTable[$index][1]), 4);
fseek($this->filePointer, 4096 + ($index << 2));
fwrite($this->filePointer, Binary::writeInt($this->locationTable[$index][2]), 4);
}
private function createBlank(){
protected function createBlank(){
fseek($this->filePointer, 0);
ftruncate($this->filePointer, 0);
$this->lastSector = 1;