mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-21 08:17:34 +00:00
Introduce dedicated NBT data exceptions, fix up some corrupted chunk handling
This commit is contained in:
parent
c5998a92a8
commit
6b7710e62b
8
composer.lock
generated
8
composer.lock
generated
@ -372,12 +372,12 @@
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/NBT.git",
|
||||
"reference": "8b211471159b4aca329c665bdbd9149046ca6550"
|
||||
"reference": "6736e85ec7600309d7ef1dc5b965ff7e4b1c5357"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/NBT/zipball/8b211471159b4aca329c665bdbd9149046ca6550",
|
||||
"reference": "8b211471159b4aca329c665bdbd9149046ca6550",
|
||||
"url": "https://api.github.com/repos/pmmp/NBT/zipball/6736e85ec7600309d7ef1dc5b965ff7e4b1c5357",
|
||||
"reference": "6736e85ec7600309d7ef1dc5b965ff7e4b1c5357",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -405,7 +405,7 @@
|
||||
"source": "https://github.com/pmmp/NBT/tree/master",
|
||||
"issues": "https://github.com/pmmp/NBT/issues"
|
||||
},
|
||||
"time": "2019-01-09T00:12:13+00:00"
|
||||
"time": "2019-01-18T15:28:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/raklib",
|
||||
|
@ -88,10 +88,12 @@ use pocketmine\level\Level;
|
||||
use pocketmine\level\Position;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\metadata\MetadataValue;
|
||||
use pocketmine\nbt\NbtDataException;
|
||||
use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\DoubleTag;
|
||||
use pocketmine\nbt\tag\ListTag;
|
||||
use pocketmine\network\BadPacketException;
|
||||
use pocketmine\network\mcpe\CompressBatchPromise;
|
||||
use pocketmine\network\mcpe\NetworkCipher;
|
||||
use pocketmine\network\mcpe\NetworkNbtSerializer;
|
||||
@ -2451,6 +2453,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return $handled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BlockEntityDataPacket $packet
|
||||
*
|
||||
* @return bool
|
||||
* @throws BadPacketException
|
||||
*/
|
||||
public function handleBlockEntityData(BlockEntityDataPacket $packet) : bool{
|
||||
$this->doCloseInventory();
|
||||
|
||||
@ -2462,7 +2470,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$t = $this->level->getTile($pos);
|
||||
if($t instanceof Spawnable){
|
||||
$nbt = new NetworkNbtSerializer();
|
||||
$compound = $nbt->read($packet->namedtag);
|
||||
try{
|
||||
$compound = $nbt->read($packet->namedtag);
|
||||
}catch(NbtDataException $e){
|
||||
throw new BadPacketException($e->getMessage(), 0, $e);
|
||||
}
|
||||
if(!$t->updateCompoundTag($compound, $this)){
|
||||
$t->spawnTo($this);
|
||||
}
|
||||
|
@ -59,6 +59,7 @@ use pocketmine\metadata\LevelMetadataStore;
|
||||
use pocketmine\metadata\PlayerMetadataStore;
|
||||
use pocketmine\nbt\BigEndianNbtSerializer;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\NbtDataException;
|
||||
use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\DoubleTag;
|
||||
@ -676,15 +677,13 @@ class Server{
|
||||
if($this->shouldSavePlayerData()){
|
||||
if(file_exists($path . "$name.dat")){
|
||||
try{
|
||||
$nbt = new BigEndianNbtSerializer();
|
||||
return $nbt->readCompressed(file_get_contents($path . "$name.dat"));
|
||||
}catch(\Throwable $e){ //zlib decode error / corrupt data
|
||||
return (new BigEndianNbtSerializer())->readCompressed(file_get_contents($path . "$name.dat"));
|
||||
}catch(NbtDataException $e){ //zlib decode error / corrupt data
|
||||
rename($path . "$name.dat", $path . "$name.dat.bak");
|
||||
$this->logger->notice($this->getLanguage()->translateString("pocketmine.data.playerCorrupted", [$name]));
|
||||
$this->logger->error($this->getLanguage()->translateString("pocketmine.data.playerCorrupted", [$name]));
|
||||
}
|
||||
}else{
|
||||
$this->logger->notice($this->getLanguage()->translateString("pocketmine.data.playerNotFound", [$name]));
|
||||
}
|
||||
$this->logger->notice($this->getLanguage()->translateString("pocketmine.data.playerNotFound", [$name]));
|
||||
}
|
||||
$spawn = $this->levelManager->getDefaultLevel()->getSafeSpawn();
|
||||
$currentTimeMillis = (int) (microtime(true) * 1000);
|
||||
|
@ -29,7 +29,7 @@ use pocketmine\command\utils\InvalidCommandSyntaxException;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\lang\TranslationContainer;
|
||||
use pocketmine\nbt\JsonNbtParser;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\NbtDataException;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use function array_slice;
|
||||
use function count;
|
||||
@ -75,16 +75,11 @@ class GiveCommand extends VanillaCommand{
|
||||
}
|
||||
|
||||
if(isset($args[3])){
|
||||
$tags = $exception = null;
|
||||
$data = implode(" ", array_slice($args, 3));
|
||||
try{
|
||||
$tags = JsonNbtParser::parseJson($data);
|
||||
}catch(\Exception $ex){
|
||||
$exception = $ex;
|
||||
}
|
||||
|
||||
if(!($tags instanceof CompoundTag) or $exception !== null){
|
||||
$sender->sendMessage(new TranslationContainer("commands.give.tagError", [$exception !== null ? $exception->getMessage() : "Invalid tag conversion"]));
|
||||
}catch(NbtDataException $e){
|
||||
$sender->sendMessage(new TranslationContainer("commands.give.tagError", [$e->getMessage()]));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,7 @@ use pocketmine\item\enchantment\EnchantmentInstance;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\LittleEndianNbtSerializer;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\NbtDataException;
|
||||
use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\ListTag;
|
||||
@ -64,11 +65,13 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
/** @var LittleEndianNbtSerializer */
|
||||
private static $cachedParser = null;
|
||||
|
||||
/**
|
||||
* @param string $tag
|
||||
*
|
||||
* @return CompoundTag
|
||||
* @throws NbtDataException
|
||||
*/
|
||||
private static function parseCompoundTag(string $tag) : CompoundTag{
|
||||
if($tag === ""){
|
||||
throw new \InvalidArgumentException("No NBT data found in supplied string");
|
||||
}
|
||||
|
||||
if(self::$cachedParser === null){
|
||||
self::$cachedParser = new LittleEndianNbtSerializer();
|
||||
}
|
||||
|
@ -27,13 +27,16 @@ use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\format\io\BaseLevelProvider;
|
||||
use pocketmine\level\format\io\ChunkUtils;
|
||||
use pocketmine\level\format\io\data\BedrockLevelData;
|
||||
use pocketmine\level\format\io\exception\CorruptedChunkException;
|
||||
use pocketmine\level\format\io\exception\UnsupportedChunkFormatException;
|
||||
use pocketmine\level\format\io\exception\UnsupportedLevelFormatException;
|
||||
use pocketmine\level\format\io\LevelData;
|
||||
use pocketmine\level\format\SubChunk;
|
||||
use pocketmine\nbt\LittleEndianNbtSerializer;
|
||||
use pocketmine\nbt\NbtDataException;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\BinaryDataException;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
use function array_values;
|
||||
use function chr;
|
||||
@ -130,6 +133,7 @@ class LevelDB extends BaseLevelProvider{
|
||||
* @param int $chunkZ
|
||||
*
|
||||
* @return Chunk|null
|
||||
* @throws CorruptedChunkException
|
||||
* @throws UnsupportedChunkFormatException
|
||||
*/
|
||||
protected function readChunk(int $chunkX, int $chunkZ) : ?Chunk{
|
||||
@ -164,21 +168,28 @@ class LevelDB extends BaseLevelProvider{
|
||||
continue;
|
||||
}
|
||||
|
||||
$binaryStream->setBuffer($data, 0);
|
||||
$binaryStream->setBuffer($data);
|
||||
if($binaryStream->feof()){
|
||||
throw new CorruptedChunkException("Unexpected empty data for subchunk $y");
|
||||
}
|
||||
$subChunkVersion = $binaryStream->getByte();
|
||||
|
||||
switch($subChunkVersion){
|
||||
case 0:
|
||||
$blocks = $binaryStream->get(4096);
|
||||
$blockData = $binaryStream->get(2048);
|
||||
if($chunkVersion < 4){
|
||||
$blockSkyLight = $binaryStream->get(2048);
|
||||
$blockLight = $binaryStream->get(2048);
|
||||
}else{
|
||||
//Mojang didn't bother changing the subchunk version when they stopped saving sky light -_-
|
||||
$blockSkyLight = "";
|
||||
$blockLight = "";
|
||||
$lightPopulated = false;
|
||||
try{
|
||||
$blocks = $binaryStream->get(4096);
|
||||
$blockData = $binaryStream->get(2048);
|
||||
if($chunkVersion < 4){
|
||||
$blockSkyLight = $binaryStream->get(2048);
|
||||
$blockLight = $binaryStream->get(2048);
|
||||
}else{
|
||||
//Mojang didn't bother changing the subchunk version when they stopped saving sky light -_-
|
||||
$blockSkyLight = "";
|
||||
$blockLight = "";
|
||||
$lightPopulated = false;
|
||||
}
|
||||
}catch(BinaryDataException $e){
|
||||
throw new CorruptedChunkException($e->getMessage(), 0, $e);
|
||||
}
|
||||
|
||||
$subChunks[$y] = new SubChunk($blocks, $blockData, $blockSkyLight, $blockLight);
|
||||
@ -190,18 +201,30 @@ class LevelDB extends BaseLevelProvider{
|
||||
}
|
||||
|
||||
if(($maps2d = $this->db->get($index . self::TAG_DATA_2D)) !== false){
|
||||
$binaryStream->setBuffer($maps2d, 0);
|
||||
$binaryStream->setBuffer($maps2d);
|
||||
|
||||
$heightMap = array_values(unpack("v*", $binaryStream->get(512)));
|
||||
$biomeIds = $binaryStream->get(256);
|
||||
try{
|
||||
$heightMap = array_values(unpack("v*", $binaryStream->get(512)));
|
||||
$biomeIds = $binaryStream->get(256);
|
||||
}catch(BinaryDataException $e){
|
||||
throw new CorruptedChunkException($e->getMessage(), 0, $e);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2: // < MCPE 1.0
|
||||
$binaryStream->setBuffer($this->db->get($index . self::TAG_LEGACY_TERRAIN));
|
||||
$fullIds = $binaryStream->get(32768);
|
||||
$fullData = $binaryStream->get(16384);
|
||||
$fullSkyLight = $binaryStream->get(16384);
|
||||
$fullBlockLight = $binaryStream->get(16384);
|
||||
$legacyTerrain = $this->db->get($index . self::TAG_LEGACY_TERRAIN);
|
||||
if($legacyTerrain === false){
|
||||
throw new CorruptedChunkException("Missing expected LEGACY_TERRAIN tag for format version $chunkVersion");
|
||||
}
|
||||
$binaryStream->setBuffer($legacyTerrain);
|
||||
try{
|
||||
$fullIds = $binaryStream->get(32768);
|
||||
$fullData = $binaryStream->get(16384);
|
||||
$fullSkyLight = $binaryStream->get(16384);
|
||||
$fullBlockLight = $binaryStream->get(16384);
|
||||
}catch(BinaryDataException $e){
|
||||
throw new CorruptedChunkException($e->getMessage(), 0, $e);
|
||||
}
|
||||
|
||||
for($yy = 0; $yy < 8; ++$yy){
|
||||
$subOffset = ($yy << 4);
|
||||
@ -231,8 +254,12 @@ class LevelDB extends BaseLevelProvider{
|
||||
$subChunks[$yy] = new SubChunk($ids, $data, $skyLight, $blockLight);
|
||||
}
|
||||
|
||||
$heightMap = array_values(unpack("C*", $binaryStream->get(256)));
|
||||
$biomeIds = ChunkUtils::convertBiomeColors(array_values(unpack("N*", $binaryStream->get(1024))));
|
||||
try{
|
||||
$heightMap = array_values(unpack("C*", $binaryStream->get(256)));
|
||||
$biomeIds = ChunkUtils::convertBiomeColors(array_values(unpack("N*", $binaryStream->get(1024))));
|
||||
}catch(BinaryDataException $e){
|
||||
throw new CorruptedChunkException($e->getMessage(), 0, $e);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//TODO: set chunks read-only so the version on disk doesn't get overwritten
|
||||
@ -244,13 +271,21 @@ class LevelDB extends BaseLevelProvider{
|
||||
/** @var CompoundTag[] $entities */
|
||||
$entities = [];
|
||||
if(($entityData = $this->db->get($index . self::TAG_ENTITY)) !== false and $entityData !== ""){
|
||||
$entities = $nbt->readMultiple($entityData);
|
||||
try{
|
||||
$entities = $nbt->readMultiple($entityData);
|
||||
}catch(NbtDataException $e){
|
||||
throw new CorruptedChunkException($e->getMessage(), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/** @var CompoundTag[] $tiles */
|
||||
$tiles = [];
|
||||
if(($tileData = $this->db->get($index . self::TAG_BLOCK_ENTITY)) !== false and $tileData !== ""){
|
||||
$tiles = $nbt->readMultiple($tileData);
|
||||
try{
|
||||
$tiles = $nbt->readMultiple($tileData);
|
||||
}catch(NbtDataException $e){
|
||||
throw new CorruptedChunkException($e->getMessage(), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: extra data should be converted into blockstorage layers (first they need to be implemented!)
|
||||
|
@ -29,6 +29,7 @@ use pocketmine\level\format\io\exception\CorruptedChunkException;
|
||||
use pocketmine\level\format\SubChunk;
|
||||
use pocketmine\nbt\BigEndianNbtSerializer;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\NbtDataException;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\IntArrayTag;
|
||||
use pocketmine\nbt\tag\ListTag;
|
||||
@ -95,9 +96,19 @@ trait LegacyAnvilChunkTrait{
|
||||
|
||||
abstract protected function serializeSubChunk(SubChunk $subChunk) : CompoundTag;
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
*
|
||||
* @return Chunk
|
||||
* @throws CorruptedChunkException
|
||||
*/
|
||||
protected function deserializeChunk(string $data) : Chunk{
|
||||
$nbt = new BigEndianNbtSerializer();
|
||||
$chunk = $nbt->readCompressed($data);
|
||||
try{
|
||||
$chunk = $nbt->readCompressed($data);
|
||||
}catch(NbtDataException $e){
|
||||
throw new CorruptedChunkException($e->getMessage(), 0, $e);
|
||||
}
|
||||
if(!$chunk->hasTag("Level")){
|
||||
throw new CorruptedChunkException("'Level' key is missing from chunk NBT");
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ use pocketmine\level\format\io\exception\CorruptedChunkException;
|
||||
use pocketmine\level\format\SubChunk;
|
||||
use pocketmine\nbt\BigEndianNbtSerializer;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\NbtDataException;
|
||||
use pocketmine\nbt\tag\ByteArrayTag;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\IntArrayTag;
|
||||
@ -107,7 +108,11 @@ class McRegion extends RegionLevelProvider{
|
||||
*/
|
||||
protected function deserializeChunk(string $data) : Chunk{
|
||||
$nbt = new BigEndianNbtSerializer();
|
||||
$chunk = $nbt->readCompressed($data);
|
||||
try{
|
||||
$chunk = $nbt->readCompressed($data);
|
||||
}catch(NbtDataException $e){
|
||||
throw new CorruptedChunkException($e->getMessage(), 0, $e);
|
||||
}
|
||||
if(!$chunk->hasTag("Level")){
|
||||
throw new CorruptedChunkException("'Level' key is missing from chunk NBT");
|
||||
}
|
||||
|
@ -176,6 +176,13 @@ abstract class RegionLevelProvider extends BaseLevelProvider{
|
||||
*/
|
||||
abstract protected function deserializeChunk(string $data) : Chunk;
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
*
|
||||
* @return Chunk|null
|
||||
* @throws CorruptedChunkException
|
||||
*/
|
||||
protected function readChunk(int $chunkX, int $chunkZ) : ?Chunk{
|
||||
$regionX = $regionZ = null;
|
||||
self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ);
|
||||
|
@ -31,6 +31,7 @@ use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\LittleEndianNbtSerializer;
|
||||
use pocketmine\nbt\NbtDataException;
|
||||
use pocketmine\network\BadPacketException;
|
||||
use pocketmine\network\mcpe\protocol\types\CommandOriginData;
|
||||
use pocketmine\network\mcpe\protocol\types\EntityLink;
|
||||
@ -105,7 +106,7 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
}
|
||||
try{
|
||||
$compound = self::$itemNbtSerializer->read($this->get($nbtLen));
|
||||
}catch(\UnexpectedValueException $e){
|
||||
}catch(NbtDataException $e){
|
||||
throw new BadPacketException($e->getMessage(), 0, $e);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user