PocketMine-MP  1.4 - API 1.10.0
 All Classes Namespaces Functions Variables Pages
anvil/RegionLoader.php
1 <?php
2 
3 /*
4  *
5  * ____ _ _ __ __ _ __ __ ____
6  * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
7  * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
8  * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
9  * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * @author PocketMine Team
17  * @link http://www.pocketmine.net/
18  *
19  *
20 */
21 
22 namespace pocketmine\level\format\anvil;
23 
35 
37  const VERSION = 1;
38  const COMPRESSION_GZIP = 1;
39  const COMPRESSION_ZLIB = 2;
40  public static $COMPRESSION_LEVEL = 7;
41 
42  public function __construct(LevelProvider $level, $regionX, $regionZ){
43  $this->x = $regionX;
44  $this->z = $regionZ;
45  $this->levelProvider = $level;
46  $this->filePath = $this->levelProvider->getPath() . "region/r.$regionX.$regionZ.mca";
47  $exists = file_exists($this->filePath);
48  touch($this->filePath);
49  $this->filePointer = fopen($this->filePath, "r+b");
50  stream_set_read_buffer($this->filePointer, 1024 * 16); //16KB
51  stream_set_write_buffer($this->filePointer, 1024 * 16); //16KB
52  if(!$exists){
53  $this->createBlank();
54  }else{
55  $this->loadLocationTable();
56  }
57  }
58 
59  public function readChunk($x, $z, $generate = true, $forward = false){
60  $index = self::getChunkOffset($x, $z);
61  if($index < 0 or $index >= 4096){
62  //Regenerate chunk due to corruption
63  $this->locationTable[$index][0] = 0;
64  $this->locationTable[$index][1] = 1;
65  }
66 
67  if(!$this->isChunkGenerated($index)){
68  if($generate === true){
69  //Allocate space
70  $this->locationTable[$index][0] = ++$this->lastSector;
71  $this->locationTable[$index][1] = 1;
72  fseek($this->filePointer, $this->locationTable[$index][0] << 12);
73  fwrite($this->filePointer, str_pad(Binary::writeInt(-1) . chr(self::COMPRESSION_ZLIB), 4096, "\x00", STR_PAD_RIGHT));
74  $this->writeLocationIndex($index);
75  }else{
76  return false;
77  }
78  }
79 
80  fseek($this->filePointer, $this->locationTable[$index][0] << 12);
81  $length = Binary::readInt(fread($this->filePointer, 4));
82  $compression = ord(fgetc($this->filePointer));
83 
84  if($length <= 0){ //Not yet generated
85  $this->generateChunk($x, $z);
86  fseek($this->filePointer, $this->locationTable[$index][0] << 12);
87  $length = Binary::readInt(fread($this->filePointer, 4));
88  $compression = ord(fgetc($this->filePointer));
89  }
90 
91  if($length >= self::MAX_SECTOR_LENGTH){
92  MainLogger::getLogger()->error("Corrupted chunk header detected");
93 
94  return false;
95  }
96 
97  if($length > ($this->locationTable[$index][1] << 12)){ //Invalid chunk, bigger than defined number of sectors
98  MainLogger::getLogger()->error("Corrupted chunk detected");
99  $this->locationTable[$index][1] = $length >> 12;
100  $this->writeLocationIndex($index);
101  }elseif($compression !== self::COMPRESSION_ZLIB and $compression !== self::COMPRESSION_GZIP){
102  MainLogger::getLogger()->error("Invalid compression type");
103 
104  return false;
105  }
106 
107  $chunk = Chunk::fromBinary(fread($this->filePointer, $length - 1), $this->levelProvider);
108  if($chunk instanceof Chunk){
109  return $chunk;
110  }elseif($forward === false){
111  MainLogger::getLogger()->error("Corrupted chunk detected");
112  $this->generateChunk($x, $z);
113 
114  return $this->readChunk($x, $z, $generate, true);
115  }else{
116  return null;
117  }
118  }
119 
120  public function generateChunk($x, $z){
121  $nbt = new Compound("Level", []);
122  $nbt->xPos = new Int("xPos", ($this->getX() * 32) + $x);
123  $nbt->zPos = new Int("zPos", ($this->getZ() * 32) + $z);
124  $nbt->LastUpdate = new Long("LastUpdate", 0);
125  $nbt->LightPopulated = new Byte("LightPopulated", 0);
126  $nbt->TerrainPopulated = new Byte("TerrainPopulated", 0);
127  $nbt->V = new Byte("V", self::VERSION);
128  $nbt->InhabitedTime = new Long("InhabitedTime", 0);
129  $nbt->Biomes = new ByteArray("Biomes", str_repeat(Binary::writeByte(-1), 256));
130  $nbt->BiomeColors = new IntArray("BiomeColors", array_fill(0, 156, Binary::readInt("\x00\x85\xb2\x4a")));
131  $nbt->HeightMap = new IntArray("HeightMap", array_fill(0, 256, 127));
132  $nbt->Sections = new Enum("Sections", []);
133  $nbt->Sections->setTagType(NBT::TAG_Compound);
134  $nbt->Entities = new Enum("Entities", []);
135  $nbt->Entities->setTagType(NBT::TAG_Compound);
136  $nbt->TileEntities = new Enum("TileEntities", []);
137  $nbt->TileEntities->setTagType(NBT::TAG_Compound);
138  $nbt->TileTicks = new Enum("TileTicks", []);
139  $nbt->TileTicks->setTagType(NBT::TAG_Compound);
140  $writer = new NBT(NBT::BIG_ENDIAN);
141  $nbt->setName("Level");
142  $writer->setData(new Compound("", ["Level" => $nbt]));
143  $chunkData = $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL);
144  $this->saveChunk($x, $z, $chunkData);
145  }
146 
147 }
static writeByte($c)
Definition: Binary.php:248
static fromBinary($data, LevelProvider $provider=null)