GenisysPro  for Minecraft PE/Windows 10 v1.1.x
Feature-rich server software for Minecraft PE and Windows 10 Edition
RegionLoader Class Reference

Public Member Functions

 __construct (McRegion $level, int $regionX, int $regionZ, string $fileExtension=McRegion::REGION_FILE_EXTENSION)
 
 __destruct ()
 
 readChunk (int $x, int $z)
 
 chunkExists (int $x, int $z)
 
 removeChunk (int $x, int $z)
 
 writeChunk (Chunk $chunk)
 
 close ()
 
 doSlowCleanUp ()
 
 getX ()
 
 getZ ()
 

Data Fields

const VERSION = 1
 
const COMPRESSION_GZIP = 1
 
const COMPRESSION_ZLIB = 2
 
const MAX_SECTOR_LENGTH = 256 << 12
 
 $lastUsed
 

Static Public Attributes

static $COMPRESSION_LEVEL = 7
 

Protected Member Functions

 isChunkGenerated (int $index)
 
 saveChunk (int $x, int $z, string $chunkData)
 
 loadLocationTable ()
 
 writeLocationIndex ($index)
 
 createBlank ()
 

Static Protected Member Functions

static getChunkOffset (int $x, int $z)
 

Protected Attributes

 $x
 
 $z
 
 $filePath
 
 $filePointer
 
 $lastSector
 
 $levelProvider
 
 $locationTable = []
 

Constructor & Destructor Documentation

◆ __construct()

__construct ( McRegion  $level,
int  $regionX,
int  $regionZ,
string  $fileExtension = McRegion::REGION_FILE_EXTENSION 
)

RegionLoader constructor.

Parameters
McRegion$level
int$regionX
int$regionZ
string$fileExtension
57  {
58  $this->x = $regionX;
59  $this->z = $regionZ;
60  $this->levelProvider = $level;
61  $this->filePath = $this->levelProvider->getPath() . "region/r.$regionX.$regionZ.$fileExtension";
62  $exists = file_exists($this->filePath);
63  if(!$exists){
64  touch($this->filePath);
65  }
66  $this->filePointer = fopen($this->filePath, "r+b");
67  stream_set_read_buffer($this->filePointer, 1024 * 16); //16KB
68  stream_set_write_buffer($this->filePointer, 1024 * 16); //16KB
69  if(!$exists){
70  $this->createBlank();
71  }else{
72  $this->loadLocationTable();
73  }
74 
75  $this->lastUsed = time();
76  }

◆ __destruct()

__destruct ( )
78  {
79  if(is_resource($this->filePointer)){
80  $this->writeLocationTable();
81  fclose($this->filePointer);
82  }
83  }

Member Function Documentation

◆ chunkExists()

chunkExists ( int  $x,
int  $z 
)
Parameters
int$x
int$z
Returns
bool
149  : bool{
150  return $this->isChunkGenerated(self::getChunkOffset($x, $z));
151  }

◆ close()

close ( )
216  {
217  $this->writeLocationTable();
218  fclose($this->filePointer);
219  $this->levelProvider = null;
220  }

◆ createBlank()

createBlank ( )
protected
340  {
341  fseek($this->filePointer, 0);
342  ftruncate($this->filePointer, 0);
343  $this->lastSector = 1;
344  $table = "";
345  for($i = 0; $i < 1024; ++$i){
346  $this->locationTable[$i] = [0, 0];
347  $table .= Binary::writeInt(0);
348  }
349 
350  $time = time();
351  for($i = 0; $i < 1024; ++$i){
352  $this->locationTable[$i][2] = $time;
353  $table .= Binary::writeInt($time);
354  }
355 
356  fwrite($this->filePointer, $table, 4096 * 2);
357  }

◆ doSlowCleanUp()

doSlowCleanUp ( )
Returns
int
225  : int{
226  for($i = 0; $i < 1024; ++$i){
227  if($this->locationTable[$i][0] === 0 or $this->locationTable[$i][1] === 0){
228  continue;
229  }
230  fseek($this->filePointer, $this->locationTable[$i][0] << 12);
231  $chunk = fread($this->filePointer, $this->locationTable[$i][1] << 12);
232  $length = Binary::readInt(substr($chunk, 0, 4));
233  if($length <= 1){
234  $this->locationTable[$i] = [0, 0, 0]; //Non-generated chunk, remove it from index
235  }
236 
237  try{
238  $chunk = zlib_decode(substr($chunk, 5));
239  }catch(\Throwable $e){
240  $this->locationTable[$i] = [0, 0, 0]; //Corrupted chunk, remove it
241  continue;
242  }
243 
244  $chunk = chr(self::COMPRESSION_ZLIB) . zlib_encode($chunk, ZLIB_ENCODING_DEFLATE, 9);
245  $chunk = Binary::writeInt(strlen($chunk)) . $chunk;
246  $sectors = (int) ceil(strlen($chunk) / 4096);
247  if($sectors > $this->locationTable[$i][1]){
248  $this->locationTable[$i][0] = $this->lastSector + 1;
249  $this->lastSector += $sectors;
250  }
251  fseek($this->filePointer, $this->locationTable[$i][0] << 12);
252  fwrite($this->filePointer, str_pad($chunk, $sectors << 12, "\x00", STR_PAD_RIGHT));
253  }
254  $this->writeLocationTable();
255  $n = $this->cleanGarbage();
256  $this->writeLocationTable();
257 
258  return $n;
259  }

◆ getChunkOffset()

static getChunkOffset ( int  $x,
int  $z 
)
staticprotected
Parameters
int$x
int$z
Returns
int
212  : int{
213  return $x + ($z << 5);
214  }

◆ getX()

getX ( )
Returns
int
362  : int{
363  return $this->x;
364  }

◆ getZ()

getZ ( )
Returns
int
369  : int{
370  return $this->z;
371  }

◆ isChunkGenerated()

isChunkGenerated ( int  $index)
protected
Parameters
int$index
Returns
bool
90  : bool{
91  return !($this->locationTable[$index][0] === 0 or $this->locationTable[$index][1] === 0);
92  }

◆ loadLocationTable()

loadLocationTable ( )
protected
303  {
304  fseek($this->filePointer, 0);
305  $this->lastSector = 1;
306 
307  $data = unpack("N*", fread($this->filePointer, 4 * 1024 * 2)); //1024 records * 4 bytes * 2 times
308  for($i = 0; $i < 1024; ++$i){
309  $index = $data[$i + 1];
310  $this->locationTable[$i] = [$index >> 8, $index & 0xff, $data[1024 + $i + 1]];
311  if(($this->locationTable[$i][0] + $this->locationTable[$i][1] - 1) > $this->lastSector){
312  $this->lastSector = $this->locationTable[$i][0] + $this->locationTable[$i][1] - 1;
313  }
314  }
315  }

◆ readChunk()

readChunk ( int  $x,
int  $z 
)
Parameters
int$x
int$z
Returns
null|Chunk
100  {
101  $index = self::getChunkOffset($x, $z);
102  if($index < 0 or $index >= 4096){
103  return null;
104  }
105 
106  $this->lastUsed = time();
107 
108  if(!$this->isChunkGenerated($index)){
109  return null;
110  }
111 
112  fseek($this->filePointer, $this->locationTable[$index][0] << 12);
113  $length = Binary::readInt(fread($this->filePointer, 4));
114  $compression = ord(fgetc($this->filePointer));
115 
116  if($length <= 0 or $length > self::MAX_SECTOR_LENGTH){ //Not yet generated / corrupted
117  if($length >= self::MAX_SECTOR_LENGTH){
118  $this->locationTable[$index][0] = ++$this->lastSector;
119  $this->locationTable[$index][1] = 1;
120  MainLogger::getLogger()->error("Corrupted chunk header detected");
121  }
122  return null;
123  }
124 
125  if($length > ($this->locationTable[$index][1] << 12)){ //Invalid chunk, bigger than defined number of sectors
126  MainLogger::getLogger()->error("Corrupted bigger chunk detected");
127  $this->locationTable[$index][1] = $length >> 12;
128  $this->writeLocationIndex($index);
129  }elseif($compression !== self::COMPRESSION_ZLIB and $compression !== self::COMPRESSION_GZIP){
130  MainLogger::getLogger()->error("Invalid compression type");
131  return null;
132  }
133 
134  $chunk = $this->levelProvider->nbtDeserialize(fread($this->filePointer, $length - 1));
135  if($chunk instanceof Chunk){
136  return $chunk;
137  }else{
138  MainLogger::getLogger()->error("Corrupted chunk detected");
139  return null;
140  }
141  }

◆ removeChunk()

removeChunk ( int  $x,
int  $z 
)
Parameters
int$x
int$z
189  {
190  $index = self::getChunkOffset($x, $z);
191  $this->locationTable[$index][0] = 0;
192  $this->locationTable[$index][1] = 0;
193  }

◆ saveChunk()

saveChunk ( int  $x,
int  $z,
string  $chunkData 
)
protected
Parameters
int$x
int$z
string$chunkData
158  {
159  $length = strlen($chunkData) + 1;
160  if($length + 4 > self::MAX_SECTOR_LENGTH){
161  throw new ChunkException("Chunk is too big! " . ($length + 4) . " > " . self::MAX_SECTOR_LENGTH);
162  }
163  $sectors = (int) ceil(($length + 4) / 4096);
164  $index = self::getChunkOffset($x, $z);
165  $indexChanged = false;
166  if($this->locationTable[$index][1] < $sectors){
167  $this->locationTable[$index][0] = $this->lastSector + 1;
168  $this->lastSector += $sectors; //The GC will clean this shift "later"
169  $indexChanged = true;
170  }elseif($this->locationTable[$index][1] != $sectors){
171  $indexChanged = true;
172  }
173 
174  $this->locationTable[$index][1] = $sectors;
175  $this->locationTable[$index][2] = time();
176 
177  fseek($this->filePointer, $this->locationTable[$index][0] << 12);
178  fwrite($this->filePointer, str_pad(Binary::writeInt($length) . chr(self::COMPRESSION_ZLIB) . $chunkData, $sectors << 12, "\x00", STR_PAD_RIGHT));
179 
180  if($indexChanged){
181  $this->writeLocationIndex($index);
182  }
183  }

◆ writeChunk()

writeChunk ( Chunk  $chunk)
Parameters
Chunk$chunk
198  {
199  $this->lastUsed = time();
200  $chunkData = $this->levelProvider->nbtSerialize($chunk);
201  if($chunkData !== false){
202  $this->saveChunk($chunk->getX() - ($this->getX() * 32), $chunk->getZ() - ($this->getZ() * 32), $chunkData);
203  }
204  }

◆ writeLocationIndex()

writeLocationIndex (   $index)
protected
Parameters
$index
333  {
334  fseek($this->filePointer, $index << 2);
335  fwrite($this->filePointer, Binary::writeInt(($this->locationTable[$index][0] << 8) | $this->locationTable[$index][1]), 4);
336  fseek($this->filePointer, 4096 + ($index << 2));
337  fwrite($this->filePointer, Binary::writeInt($this->locationTable[$index][2]), 4);
338  }

Field Documentation

◆ $COMPRESSION_LEVEL

$COMPRESSION_LEVEL = 7
static

◆ $filePath

$filePath
protected

◆ $filePointer

$filePointer
protected

◆ $lastSector

$lastSector
protected

◆ $lastUsed

$lastUsed

◆ $levelProvider

$levelProvider
protected

◆ $locationTable

$locationTable = []
protected

◆ $x

$x
protected

◆ $z

$z
protected

◆ COMPRESSION_GZIP

const COMPRESSION_GZIP = 1

◆ COMPRESSION_ZLIB

const COMPRESSION_ZLIB = 2

◆ MAX_SECTOR_LENGTH

const MAX_SECTOR_LENGTH = 256 << 12

◆ VERSION

const VERSION = 1

The documentation for this class was generated from the following file: