mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-05-13 01:09:44 +00:00
RegionLoader: Use objects instead of arrays
This commit is contained in:
parent
4d9b074641
commit
07a9c35ee2
@ -26,7 +26,6 @@ namespace pocketmine\level\format\io\region;
|
||||
use pocketmine\level\format\ChunkException;
|
||||
use pocketmine\level\format\io\exception\CorruptedChunkException;
|
||||
use pocketmine\utils\Binary;
|
||||
use function array_fill;
|
||||
use function ceil;
|
||||
use function chr;
|
||||
use function fclose;
|
||||
@ -39,6 +38,7 @@ use function fseek;
|
||||
use function ftruncate;
|
||||
use function fwrite;
|
||||
use function is_resource;
|
||||
use function max;
|
||||
use function ord;
|
||||
use function pack;
|
||||
use function str_pad;
|
||||
@ -66,7 +66,7 @@ class RegionLoader{
|
||||
protected $filePointer;
|
||||
/** @var int */
|
||||
protected $lastSector;
|
||||
/** @var int[][] [offset in sectors, chunk size in sectors, timestamp] */
|
||||
/** @var RegionLocationTableEntry[] */
|
||||
protected $locationTable = [];
|
||||
/** @var int */
|
||||
public $lastUsed = 0;
|
||||
@ -106,7 +106,7 @@ class RegionLoader{
|
||||
}
|
||||
|
||||
protected function isChunkGenerated(int $index) : bool{
|
||||
return !($this->locationTable[$index][0] === 0 or $this->locationTable[$index][1] === 0);
|
||||
return !$this->locationTable[$index]->isNull();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -126,23 +126,25 @@ class RegionLoader{
|
||||
return null;
|
||||
}
|
||||
|
||||
fseek($this->filePointer, $this->locationTable[$index][0] << 12);
|
||||
fseek($this->filePointer, $this->locationTable[$index]->getFirstSector() << 12);
|
||||
|
||||
$prefix = fread($this->filePointer, 4);
|
||||
if($prefix === false or strlen($prefix) !== 4){
|
||||
throw new CorruptedChunkException("Corrupted chunk header detected (unexpected end of file reading length prefix)");
|
||||
}
|
||||
$length = Binary::readInt($prefix);
|
||||
|
||||
if($length <= 0 or $length > self::MAX_SECTOR_LENGTH){ //Not yet generated / corrupted
|
||||
if($length >= self::MAX_SECTOR_LENGTH){
|
||||
throw new CorruptedChunkException("Corrupted chunk header detected (sector count $length larger than max " . self::MAX_SECTOR_LENGTH . ")");
|
||||
}
|
||||
if($length <= 0){ //TODO: if we reached here, the locationTable probably needs updating
|
||||
return null;
|
||||
}
|
||||
if($length > self::MAX_SECTOR_LENGTH){ //corrupted
|
||||
throw new CorruptedChunkException("Length for chunk x=$x,z=$z ($length) is larger than maximum " . self::MAX_SECTOR_LENGTH);
|
||||
}
|
||||
|
||||
if($length > ($this->locationTable[$index][1] << 12)){ //Invalid chunk, bigger than defined number of sectors
|
||||
\GlobalLogger::get()->error("Chunk x=$x,z=$z length mismatch (expected " . ($this->locationTable[$index][1] << 12) . " sectors, got $length sectors)");
|
||||
$this->locationTable[$index][1] = $length >> 12;
|
||||
if($length > ($this->locationTable[$index]->getSectorCount() << 12)){ //Invalid chunk, bigger than defined number of sectors
|
||||
\GlobalLogger::get()->error("Chunk x=$x,z=$z length mismatch (expected " . ($this->locationTable[$index]->getSectorCount() << 12) . " sectors, got $length sectors)");
|
||||
$old = $this->locationTable[$index];
|
||||
$this->locationTable[$index] = new RegionLocationTableEntry($old->getFirstSector(), $length >> 12, time());
|
||||
$this->writeLocationIndex($index);
|
||||
}
|
||||
|
||||
@ -185,26 +187,22 @@ class RegionLoader{
|
||||
if($length + 4 > self::MAX_SECTOR_LENGTH){
|
||||
throw new ChunkException("Chunk is too big! " . ($length + 4) . " > " . self::MAX_SECTOR_LENGTH);
|
||||
}
|
||||
$sectors = (int) ceil(($length + 4) / 4096);
|
||||
|
||||
$newSize = (int) ceil(($length + 4) / 4096);
|
||||
$index = self::getChunkOffset($x, $z);
|
||||
$indexChanged = false;
|
||||
if($this->locationTable[$index][1] < $sectors){
|
||||
$this->locationTable[$index][0] = $this->lastSector + 1;
|
||||
$this->lastSector += $sectors; //The GC will clean this shift "later"
|
||||
$indexChanged = true;
|
||||
}elseif($this->locationTable[$index][1] != $sectors){
|
||||
$indexChanged = true;
|
||||
$offset = $this->locationTable[$index]->getFirstSector();
|
||||
|
||||
if($this->locationTable[$index]->getSectorCount() < $newSize){
|
||||
$offset = $this->lastSector + 1;
|
||||
}
|
||||
|
||||
$this->locationTable[$index][1] = $sectors;
|
||||
$this->locationTable[$index][2] = time();
|
||||
$this->locationTable[$index] = new RegionLocationTableEntry($offset, $newSize, time());
|
||||
$this->lastSector = max($this->lastSector, $this->locationTable[$index]->getLastSector());
|
||||
|
||||
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));
|
||||
fseek($this->filePointer, $offset << 12);
|
||||
fwrite($this->filePointer, str_pad(Binary::writeInt($length) . chr(self::COMPRESSION_ZLIB) . $chunkData, $newSize << 12, "\x00", STR_PAD_RIGHT));
|
||||
|
||||
if($indexChanged){
|
||||
$this->writeLocationIndex($index);
|
||||
}
|
||||
$this->writeLocationIndex($index);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -215,8 +213,7 @@ class RegionLoader{
|
||||
*/
|
||||
public function removeChunk(int $x, int $z) : void{
|
||||
$index = self::getChunkOffset($x, $z);
|
||||
$this->locationTable[$index][0] = 0;
|
||||
$this->locationTable[$index][1] = 0;
|
||||
$this->locationTable[$index] = new RegionLocationTableEntry(0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -276,6 +273,7 @@ class RegionLoader{
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$index = $data[$i + 1];
|
||||
$offset = $index >> 8;
|
||||
$timestamp = $data[$i + 1025];
|
||||
if($offset !== 0){
|
||||
self::getChunkCoords($i, $x, $z);
|
||||
$fileOffset = $offset << 12;
|
||||
@ -291,10 +289,8 @@ class RegionLoader{
|
||||
}
|
||||
}
|
||||
|
||||
$this->locationTable[$i] = [$index >> 8, $index & 0xff, $data[1024 + $i + 1]];
|
||||
if(($this->locationTable[$i][0] + $this->locationTable[$i][1] - 1) > $this->lastSector){
|
||||
$this->lastSector = $this->locationTable[$i][0] + $this->locationTable[$i][1] - 1;
|
||||
}
|
||||
$this->locationTable[$i] = new RegionLocationTableEntry($offset, $index & 0xff, $timestamp);
|
||||
$this->lastSector = max($this->lastSector, $this->locationTable[$i]->getLastSector());
|
||||
}
|
||||
|
||||
fseek($this->filePointer, 0);
|
||||
@ -304,10 +300,10 @@ class RegionLoader{
|
||||
$write = [];
|
||||
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$write[] = (($this->locationTable[$i][0] << 8) | $this->locationTable[$i][1]);
|
||||
$write[] = (($this->locationTable[$i]->getFirstSector() << 8) | $this->locationTable[$i]->getSectorCount());
|
||||
}
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$write[] = $this->locationTable[$i][2];
|
||||
$write[] = $this->locationTable[$i]->getTimestamp();
|
||||
}
|
||||
fseek($this->filePointer, 0);
|
||||
fwrite($this->filePointer, pack("N*", ...$write), 4096 * 2);
|
||||
@ -315,16 +311,18 @@ class RegionLoader{
|
||||
|
||||
protected function writeLocationIndex(int $index) : void{
|
||||
fseek($this->filePointer, $index << 2);
|
||||
fwrite($this->filePointer, Binary::writeInt(($this->locationTable[$index][0] << 8) | $this->locationTable[$index][1]), 4);
|
||||
fwrite($this->filePointer, Binary::writeInt(($this->locationTable[$index]->getFirstSector() << 8) | $this->locationTable[$index]->getSectorCount()), 4);
|
||||
fseek($this->filePointer, 4096 + ($index << 2));
|
||||
fwrite($this->filePointer, Binary::writeInt($this->locationTable[$index][2]), 4);
|
||||
fwrite($this->filePointer, Binary::writeInt($this->locationTable[$index]->getTimestamp()), 4);
|
||||
}
|
||||
|
||||
protected function createBlank() : void{
|
||||
fseek($this->filePointer, 0);
|
||||
ftruncate($this->filePointer, 8192); // this fills the file with the null byte
|
||||
$this->lastSector = 1;
|
||||
$this->locationTable = array_fill(0, 1024, [0, 0, 0]);
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$this->locationTable[$i] = new RegionLocationTableEntry(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public function getFilePath() : string{
|
||||
|
@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\level\format\io\region;
|
||||
|
||||
use function range;
|
||||
|
||||
class RegionLocationTableEntry{
|
||||
|
||||
/** @var int */
|
||||
private $firstSector;
|
||||
/** @var int */
|
||||
private $sectorCount;
|
||||
/** @var int */
|
||||
private $timestamp;
|
||||
|
||||
/**
|
||||
* @param int $firstSector
|
||||
* @param int $sectorCount
|
||||
* @param int $timestamp
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function __construct(int $firstSector, int $sectorCount, int $timestamp){
|
||||
if($firstSector < 0){
|
||||
throw new \InvalidArgumentException("Start sector must be positive, got $firstSector");
|
||||
}
|
||||
$this->firstSector = $firstSector;
|
||||
if($sectorCount < 0 or $sectorCount > 255){
|
||||
throw new \InvalidArgumentException("Sector count must be in range 0...255, got $sectorCount");
|
||||
}
|
||||
$this->sectorCount = $sectorCount;
|
||||
$this->timestamp = $timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getFirstSector() : int{
|
||||
return $this->firstSector;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getLastSector() : int{
|
||||
return $this->firstSector + $this->sectorCount - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of sector offsets reserved by this chunk.
|
||||
* @return int[]
|
||||
*/
|
||||
public function getUsedSectors() : array{
|
||||
return range($this->getFirstSector(), $this->getLastSector());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSectorCount() : int{
|
||||
return $this->sectorCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getTimestamp() : int{
|
||||
return $this->timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isNull() : bool{
|
||||
return $this->firstSector === 0 or $this->sectorCount === 0;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user