RegionLoader: use actual null instead of zeroed entry for non-allocated chunks

this forces the code to be properly aware of non-allocated chunks, because it'll crash with NPE if it isn't.
This commit is contained in:
Dylan K. Taylor 2020-06-15 12:02:03 +01:00
parent 745be19a56
commit 6bf840c72e
2 changed files with 24 additions and 21 deletions

View File

@ -28,6 +28,7 @@ use pocketmine\level\format\io\exception\CorruptedChunkException;
use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Binary; use pocketmine\utils\Binary;
use pocketmine\utils\MainLogger; use pocketmine\utils\MainLogger;
use function assert;
use function ceil; use function ceil;
use function chr; use function chr;
use function fclose; use function fclose;
@ -77,7 +78,7 @@ class RegionLoader{
protected $filePointer; protected $filePointer;
/** @var int */ /** @var int */
protected $nextSector = self::FIRST_SECTOR; protected $nextSector = self::FIRST_SECTOR;
/** @var RegionLocationTableEntry[] */ /** @var RegionLocationTableEntry[]|null[] */
protected $locationTable = []; protected $locationTable = [];
/** @var int */ /** @var int */
public $lastUsed = 0; public $lastUsed = 0;
@ -122,7 +123,7 @@ class RegionLoader{
} }
protected function isChunkGenerated(int $index) : bool{ protected function isChunkGenerated(int $index) : bool{
return !$this->locationTable[$index]->isNull(); return $this->locationTable[$index] !== null;
} }
/** /**
@ -134,7 +135,7 @@ class RegionLoader{
$this->lastUsed = time(); $this->lastUsed = time();
if(!$this->isChunkGenerated($index)){ if($this->locationTable[$index] === null){
return null; return null;
} }
@ -195,14 +196,14 @@ class RegionLoader{
$newSize = (int) ceil(($length + 4) / 4096); $newSize = (int) ceil(($length + 4) / 4096);
$index = self::getChunkOffset($x, $z); $index = self::getChunkOffset($x, $z);
$offset = $this->locationTable[$index]->getFirstSector();
if($this->locationTable[$index]->getSectorCount() < $newSize){ if($this->locationTable[$index] === null or $this->locationTable[$index]->getSectorCount() < $newSize){
$offset = $this->nextSector; $offset = $this->nextSector;
}else{
$offset = $this->locationTable[$index]->getFirstSector(); //reuse old location - TODO: risk of corruption during power failure
} }
$this->locationTable[$index] = new RegionLocationTableEntry($offset, $newSize, time()); $this->bumpNextFreeSector($this->locationTable[$index] = $newEntry = new RegionLocationTableEntry($offset, $newSize, time()));
$this->bumpNextFreeSector($this->locationTable[$index]);
fseek($this->filePointer, $offset << 12); fseek($this->filePointer, $offset << 12);
fwrite($this->filePointer, str_pad(Binary::writeInt($length) . chr(self::COMPRESSION_ZLIB) . $chunkData, $newSize << 12, "\x00", STR_PAD_RIGHT)); fwrite($this->filePointer, str_pad(Binary::writeInt($length) . chr(self::COMPRESSION_ZLIB) . $chunkData, $newSize << 12, "\x00", STR_PAD_RIGHT));
@ -216,7 +217,7 @@ class RegionLoader{
*/ */
public function removeChunk(int $x, int $z){ public function removeChunk(int $x, int $z){
$index = self::getChunkOffset($x, $z); $index = self::getChunkOffset($x, $z);
$this->locationTable[$index] = new RegionLocationTableEntry(0, 0, 0); $this->locationTable[$index] = null;
$this->writeLocationIndex($index); $this->writeLocationIndex($index);
} }
@ -274,10 +275,9 @@ class RegionLoader{
$timestamp = $data[$i + 1025]; $timestamp = $data[$i + 1025];
if($offset === 0){ if($offset === 0){
$this->locationTable[$i] = new RegionLocationTableEntry(0, 0, 0); $this->locationTable[$i] = null;
}else{ }else{
$this->locationTable[$i] = new RegionLocationTableEntry($offset, $index & 0xff, $timestamp); $this->bumpNextFreeSector($this->locationTable[$i] = new RegionLocationTableEntry($offset, $index & 0xff, $timestamp));
$this->bumpNextFreeSector($this->locationTable[$i]);
} }
} }
@ -295,7 +295,7 @@ class RegionLoader{
for($i = 0; $i < 1024; ++$i){ for($i = 0; $i < 1024; ++$i){
$entry = $this->locationTable[$i]; $entry = $this->locationTable[$i];
if($entry->isNull()){ if($entry === null){
continue; continue;
} }
@ -318,7 +318,11 @@ class RegionLoader{
ksort($usedOffsets, SORT_NUMERIC); ksort($usedOffsets, SORT_NUMERIC);
$prevLocationIndex = null; $prevLocationIndex = null;
foreach($usedOffsets as $startOffset => $locationTableIndex){ foreach($usedOffsets as $startOffset => $locationTableIndex){
if($this->locationTable[$locationTableIndex] === null){
continue;
}
if($prevLocationIndex !== null){ if($prevLocationIndex !== null){
assert($this->locationTable[$prevLocationIndex] !== null);
if($this->locationTable[$locationTableIndex]->overlaps($this->locationTable[$prevLocationIndex])){ if($this->locationTable[$locationTableIndex]->overlaps($this->locationTable[$prevLocationIndex])){
self::getChunkCoords($locationTableIndex, $chunkXX, $chunkZZ); self::getChunkCoords($locationTableIndex, $chunkXX, $chunkZZ);
self::getChunkCoords($prevLocationIndex, $prevChunkXX, $prevChunkZZ); self::getChunkCoords($prevLocationIndex, $prevChunkXX, $prevChunkZZ);
@ -333,10 +337,12 @@ class RegionLoader{
$write = []; $write = [];
for($i = 0; $i < 1024; ++$i){ for($i = 0; $i < 1024; ++$i){
$write[] = (($this->locationTable[$i]->getFirstSector() << 8) | $this->locationTable[$i]->getSectorCount()); $entry = $this->locationTable[$i];
$write[] = $entry !== null ? (($entry->getFirstSector() << 8) | $entry->getSectorCount()) : 0;
} }
for($i = 0; $i < 1024; ++$i){ for($i = 0; $i < 1024; ++$i){
$write[] = $this->locationTable[$i]->getTimestamp(); $entry = $this->locationTable[$i];
$write[] = $entry !== null ? $entry->getTimestamp() : 0;
} }
fseek($this->filePointer, 0); fseek($this->filePointer, 0);
fwrite($this->filePointer, pack("N*", ...$write), 4096 * 2); fwrite($this->filePointer, pack("N*", ...$write), 4096 * 2);
@ -348,10 +354,11 @@ class RegionLoader{
* @return void * @return void
*/ */
protected function writeLocationIndex($index){ protected function writeLocationIndex($index){
$entry = $this->locationTable[$index];
fseek($this->filePointer, $index << 2); fseek($this->filePointer, $index << 2);
fwrite($this->filePointer, Binary::writeInt(($this->locationTable[$index]->getFirstSector() << 8) | $this->locationTable[$index]->getSectorCount()), 4); fwrite($this->filePointer, Binary::writeInt($entry !== null ? ($entry->getFirstSector() << 8) | $entry->getSectorCount() : 0), 4);
fseek($this->filePointer, 4096 + ($index << 2)); fseek($this->filePointer, 4096 + ($index << 2));
fwrite($this->filePointer, Binary::writeInt($this->locationTable[$index]->getTimestamp()), 4); fwrite($this->filePointer, Binary::writeInt($entry !== null ? $entry->getTimestamp() : 0), 4);
} }
/** /**
@ -361,7 +368,7 @@ class RegionLoader{
fseek($this->filePointer, 0); fseek($this->filePointer, 0);
ftruncate($this->filePointer, 8192); // this fills the file with the null byte ftruncate($this->filePointer, 8192); // this fills the file with the null byte
for($i = 0; $i < 1024; ++$i){ for($i = 0; $i < 1024; ++$i){
$this->locationTable[$i] = new RegionLocationTableEntry(0, 0, 0); $this->locationTable[$i] = null;
} }
} }

View File

@ -73,10 +73,6 @@ class RegionLocationTableEntry{
return $this->timestamp; return $this->timestamp;
} }
public function isNull() : bool{
return $this->firstSector === 0 or $this->sectorCount === 0;
}
public function overlaps(RegionLocationTableEntry $other) : bool{ public function overlaps(RegionLocationTableEntry $other) : bool{
$overlapCheck = static function(RegionLocationTableEntry $entry1, RegionLocationTableEntry $entry2) : bool{ $overlapCheck = static function(RegionLocationTableEntry $entry1, RegionLocationTableEntry $entry2) : bool{
$entry1Last = $entry1->getLastSector(); $entry1Last = $entry1->getLastSector();