PocketMine-MP  1.4 - API 1.10.0
 All Classes Namespaces Functions Variables Pages
McRegion.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\mcregion;
23 
37 
39 
41  protected $regions = [];
42 
44  protected $chunks = [];
45 
46  public static function getProviderName(){
47  return "mcregion";
48  }
49 
50  public static function getProviderOrder(){
51  return self::ORDER_ZXY;
52  }
53 
54  public static function usesChunkSection(){
55  return false;
56  }
57 
58  public static function isValid($path){
59  $isValid = (file_exists($path . "/level.dat") and is_dir($path . "/region/"));
60 
61  if($isValid){
62  $files = glob($path . "/region/*.mc*");
63  foreach($files as $f){
64  if(strpos($f, ".mca") !== false){ //Anvil
65  $isValid = false;
66  break;
67  }
68  }
69  }
70 
71  return $isValid;
72  }
73 
74  public static function generate($path, $name, $seed, $generator, array $options = []){
75  @mkdir($path, 0777, true);
76  @mkdir($path . "/region", 0777);
77  //TODO, add extra details
78  $levelData = new Compound("Data", [
79  "hardcore" => new Byte("hardcore", 0),
80  "initialized" => new Byte("initialized", 1),
81  "GameType" => new Int("GameType", 0),
82  "generatorVersion" => new Int("generatorVersion", 1), //2 in MCPE
83  "SpawnX" => new Int("SpawnX", 128),
84  "SpawnY" => new Int("SpawnY", 70),
85  "SpawnZ" => new Int("SpawnZ", 128),
86  "version" => new Int("version", 19133),
87  "DayTime" => new Int("DayTime", 0),
88  "LastPlayed" => new Long("LastPlayed", microtime(true) * 1000),
89  "RandomSeed" => new Long("RandomSeed", $seed),
90  "SizeOnDisk" => new Long("SizeOnDisk", 0),
91  "Time" => new Long("Time", 0),
92  "generatorName" => new String("generatorName", Generator::getGeneratorName($generator)),
93  "generatorOptions" => new String("generatorOptions", isset($options["preset"]) ? $options["preset"] : ""),
94  "LevelName" => new String("LevelName", $name),
95  "GameRules" => new Compound("GameRules", [])
96  ]);
97  $nbt = new NBT(NBT::BIG_ENDIAN);
98  $nbt->setData(new Compound(null, [
99  "Data" => $levelData
100  ]));
101  $buffer = $nbt->writeCompressed();
102  file_put_contents($path . "level.dat", $buffer);
103  }
104 
105  public static function getRegionIndex($chunkX, $chunkZ, &$x, &$z){
106  $x = $chunkX >> 5;
107  $z = $chunkZ >> 5;
108  }
109 
110  public function requestChunkTask($x, $z){
111  $chunk = $this->getChunk($x, $z, false);
112  if(!($chunk instanceof Chunk)){
113  throw new ChunkException("Invalid Chunk sent");
114  }
115 
116  $tiles = "";
117  $nbt = new NBT(NBT::LITTLE_ENDIAN);
118  foreach($chunk->getTiles() as $tile){
119  if($tile instanceof Spawnable){
120  $nbt->setData($tile->getSpawnCompound());
121  $tiles .= $nbt->write();
122  }
123  }
124 
125  $biomeColors = pack("N*", ...$chunk->getBiomeColorArray());
126 
127  $ordered = zlib_encode(
128  Binary::writeLInt($x) . Binary::writeLInt($z) .
129  $chunk->getBlockIdArray() .
130  $chunk->getBlockDataArray() .
131  $chunk->getBlockSkyLightArray() .
132  $chunk->getBlockLightArray() .
133  $chunk->getBiomeIdArray() .
134  $biomeColors .
135  $tiles
136  , ZLIB_ENCODING_DEFLATE, Level::$COMPRESSION_LEVEL);
137 
138  $this->getLevel()->chunkRequestCallback($x, $z, $ordered);
139 
140  return null;
141  }
142 
143  public function unloadChunks(){
144  foreach($this->chunks as $chunk){
145  $this->unloadChunk($chunk->getX(), $chunk->getZ(), false);
146  }
147  $this->chunks = [];
148  }
149 
150  public function getGenerator(){
151  return $this->levelData["generatorName"];
152  }
153 
154  public function getGeneratorOptions(){
155  return ["preset" => $this->levelData["generatorOptions"]];
156  }
157 
158  public function getLoadedChunks(){
159  return $this->chunks;
160  }
161 
162  public function isChunkLoaded($x, $z){
163  return isset($this->chunks[Level::chunkHash($x, $z)]);
164  }
165 
166  public function saveChunks(){
167  foreach($this->chunks as $chunk){
168  $this->saveChunk($chunk->getX(), $chunk->getZ());
169  }
170  }
171 
172  public function loadChunk($chunkX, $chunkZ, $create = false){
173  $index = Level::chunkHash($chunkX, $chunkZ);
174  if(isset($this->chunks[$index])){
175  return true;
176  }
177  $regionX = $regionZ = null;
178  self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ);
179  $this->loadRegion($regionX, $regionZ);
180  $this->level->timings->syncChunkLoadDataTimer->startTiming();
181  $chunk = $this->getRegion($regionX, $regionZ)->readChunk($chunkX - $regionX * 32, $chunkZ - $regionZ * 32, $create); //generate empty chunk if not loaded
182  $this->level->timings->syncChunkLoadDataTimer->stopTiming();
183 
184  if($chunk instanceof FullChunk){
185  $this->chunks[$index] = $chunk;
186  return true;
187  }else{
188  return false;
189  }
190  }
191 
192  public function unloadChunk($x, $z, $safe = true){
193  $chunk = isset($this->chunks[$index = Level::chunkHash($x, $z)]) ? $this->chunks[$index] : null;
194  if($chunk instanceof FullChunk and $chunk->unload(false, $safe)){
195  unset($this->chunks[$index]);
196  return true;
197  }
198 
199  return false;
200  }
201 
202  public function saveChunk($x, $z){
203  if($this->isChunkLoaded($x, $z)){
204  $this->getRegion($x >> 5, $z >> 5)->writeChunk($this->getChunk($x, $z));
205 
206  return true;
207  }
208 
209  return false;
210  }
211 
218  protected function getRegion($x, $z){
219  return isset($this->regions[$index = Level::chunkHash($x, $z)]) ? $this->regions[$index] : null;
220  }
221 
229  public function getChunk($chunkX, $chunkZ, $create = false){
230  $index = Level::chunkHash($chunkX, $chunkZ);
231  if(isset($this->chunks[$index])){
232  return $this->chunks[$index];
233  }else{
234  $this->loadChunk($chunkX, $chunkZ, $create);
235 
236  return isset($this->chunks[$index]) ? $this->chunks[$index] : null;
237  }
238  }
239 
240  public function setChunk($chunkX, $chunkZ, FullChunk $chunk){
241  if(!($chunk instanceof Chunk)){
242  throw new ChunkException("Invalid Chunk class");
243  }
244 
245  $chunk->setProvider($this);
246 
247  self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ);
248  $this->loadRegion($regionX, $regionZ);
249 
250  $chunk->setX($chunkX);
251  $chunk->setZ($chunkZ);
252 
253 
254  if(isset($this->chunks[$index = Level::chunkHash($chunkX, $chunkZ)]) and $this->chunks[$index] !== $chunk){
255  $this->unloadChunk($chunkX, $chunkZ, false);
256  }
257 
258  $this->chunks[$index] = $chunk;
259  }
260 
261  public static function createChunkSection($Y){
262  return null;
263  }
264 
265  public function isChunkGenerated($chunkX, $chunkZ){
266  if(($region = $this->getRegion($chunkX >> 5, $chunkZ >> 5)) instanceof RegionLoader){
267  return $region->chunkExists($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32) and $this->getChunk($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32, true)->isGenerated();
268  }
269 
270  return false;
271  }
272 
273  public function isChunkPopulated($chunkX, $chunkZ){
274  $chunk = $this->getChunk($chunkX, $chunkZ);
275  if($chunk instanceof FullChunk){
276  return $chunk->isPopulated();
277  }else{
278  return false;
279  }
280  }
281 
282  protected function loadRegion($x, $z){
283  if(isset($this->regions[$index = Level::chunkHash($x, $z)])){
284  return true;
285  }
286 
287  $this->regions[$index] = new RegionLoader($this, $x, $z);
288 
289  return true;
290  }
291 
292  public function close(){
293  $this->unloadChunks();
294  foreach($this->regions as $index => $region){
295  $region->close();
296  unset($this->regions[$index]);
297  }
298  $this->level = null;
299  }
300 }
unload($save=true, $safe=true)
loadChunk($chunkX, $chunkZ, $create=false)
Definition: McRegion.php:172
setProvider(LevelProvider $provider)
setChunk($chunkX, $chunkZ, FullChunk $chunk)
Definition: McRegion.php:240
getChunk($chunkX, $chunkZ, $create=false)
Definition: McRegion.php:229
static chunkHash($x, $z)
Definition: Level.php:230
static generate($path, $name, $seed, $generator, array $options=[])
Definition: McRegion.php:74