mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-05-14 09:49:50 +00:00
Merge conflict resolution
This commit is contained in:
commit
7aeb3129ac
@ -3366,11 +3366,6 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleUnknown(UnknownPacket $packet) : bool{
|
|
||||||
$this->server->getLogger()->debug("Received unknown packet from " . $this->getName() . ": 0x" . bin2hex($packet->buffer));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a packet is received from the client. This method will call DataPacketReceiveEvent.
|
* Called when a packet is received from the client. This method will call DataPacketReceiveEvent.
|
||||||
*
|
*
|
||||||
@ -3390,7 +3385,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade
|
|||||||
}
|
}
|
||||||
$this->server->getPluginManager()->callEvent($ev = new DataPacketReceiveEvent($this, $packet));
|
$this->server->getPluginManager()->callEvent($ev = new DataPacketReceiveEvent($this, $packet));
|
||||||
if(!$ev->isCancelled() and !$packet->handle($this)){
|
if(!$ev->isCancelled() and !$packet->handle($this)){
|
||||||
$this->server->getLogger()->debug("Unhandled " . get_class($packet) . " received from " . $this->getName() . ": 0x" . bin2hex($packet->buffer));
|
$this->server->getLogger()->debug("Unhandled " . $packet->getName() . " received from " . $this->getName() . ": 0x" . bin2hex($packet->buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
$timings->stopTiming();
|
$timings->stopTiming();
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit b30ca5e3bdb65c446e22bf777d1dcb04d78b6f7d
|
Subproject commit 2ab4d9172eba05cb7c6b8c98639db2ba9e60472c
|
@ -0,0 +1,28 @@
|
|||||||
|
<?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/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
namespace pocketmine\level\format\io\region;
|
||||||
|
|
||||||
|
|
||||||
|
class CorruptedRegionException extends RegionException{
|
||||||
|
|
||||||
|
}
|
@ -30,6 +30,7 @@ use pocketmine\level\format\io\ChunkUtils;
|
|||||||
use pocketmine\level\format\SubChunk;
|
use pocketmine\level\format\SubChunk;
|
||||||
use pocketmine\level\generator\Generator;
|
use pocketmine\level\generator\Generator;
|
||||||
use pocketmine\level\Level;
|
use pocketmine\level\Level;
|
||||||
|
use pocketmine\level\LevelException;
|
||||||
use pocketmine\nbt\NBT;
|
use pocketmine\nbt\NBT;
|
||||||
use pocketmine\nbt\tag\{
|
use pocketmine\nbt\tag\{
|
||||||
ByteArrayTag, ByteTag, CompoundTag, IntArrayTag, IntTag, ListTag, LongTag, StringTag
|
ByteArrayTag, ByteTag, CompoundTag, IntArrayTag, IntTag, ListTag, LongTag, StringTag
|
||||||
@ -219,12 +220,9 @@ class McRegion extends BaseLevelProvider{
|
|||||||
$isValid = (file_exists($path . "/level.dat") and is_dir($path . "/region/"));
|
$isValid = (file_exists($path . "/level.dat") and is_dir($path . "/region/"));
|
||||||
|
|
||||||
if($isValid){
|
if($isValid){
|
||||||
$files = glob($path . "/region/*.mc*");
|
|
||||||
if(empty($files)){ //possible glob() issue on some systems
|
|
||||||
$files = array_filter(scandir($path . "/region/"), function($file){
|
$files = array_filter(scandir($path . "/region/"), function($file){
|
||||||
return substr($file, strrpos($file, ".") + 1, 2) === "mc"; //region file
|
return substr($file, strrpos($file, ".") + 1, 2) === "mc"; //region file
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
foreach($files as $f){
|
foreach($files as $f){
|
||||||
if(substr($f, strrpos($f, ".") + 1) !== static::REGION_FILE_EXTENSION){
|
if(substr($f, strrpos($f, ".") + 1) !== static::REGION_FILE_EXTENSION){
|
||||||
@ -442,6 +440,22 @@ class McRegion extends BaseLevelProvider{
|
|||||||
protected function loadRegion(int $x, int $z){
|
protected function loadRegion(int $x, int $z){
|
||||||
if(!isset($this->regions[$index = Level::chunkHash($x, $z)])){
|
if(!isset($this->regions[$index = Level::chunkHash($x, $z)])){
|
||||||
$this->regions[$index] = new RegionLoader($this, $x, $z, static::REGION_FILE_EXTENSION);
|
$this->regions[$index] = new RegionLoader($this, $x, $z, static::REGION_FILE_EXTENSION);
|
||||||
|
try{
|
||||||
|
$this->regions[$index]->open();
|
||||||
|
}catch(CorruptedRegionException $e){
|
||||||
|
$logger = $this->level->getServer()->getLogger();
|
||||||
|
$logger->error("Corrupted region file detected: " . $e->getMessage());
|
||||||
|
|
||||||
|
$this->regions[$index]->close(false); //Do not write anything to the file
|
||||||
|
|
||||||
|
$path = $this->regions[$index]->getFilePath();
|
||||||
|
$backupPath = $path . ".bak." . time();
|
||||||
|
rename($path, $backupPath);
|
||||||
|
$logger->error("Corrupted region file has been backed up to " . $backupPath);
|
||||||
|
|
||||||
|
$this->regions[$index] = new RegionLoader($this, $x, $z, static::REGION_FILE_EXTENSION);
|
||||||
|
$this->regions[$index]->open(); //this will create a new empty region to replace the corrupted one
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
28
src/pocketmine/level/format/io/region/RegionException.php
Normal file
28
src/pocketmine/level/format/io/region/RegionException.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?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/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
namespace pocketmine\level\format\io\region;
|
||||||
|
|
||||||
|
|
||||||
|
class RegionException extends \RuntimeException{
|
||||||
|
|
||||||
|
}
|
@ -25,6 +25,7 @@ namespace pocketmine\level\format\io\region;
|
|||||||
|
|
||||||
use pocketmine\level\format\Chunk;
|
use pocketmine\level\format\Chunk;
|
||||||
use pocketmine\level\format\io\ChunkException;
|
use pocketmine\level\format\io\ChunkException;
|
||||||
|
use pocketmine\level\LevelException;
|
||||||
use pocketmine\utils\Binary;
|
use pocketmine\utils\Binary;
|
||||||
use pocketmine\utils\MainLogger;
|
use pocketmine\utils\MainLogger;
|
||||||
|
|
||||||
@ -32,7 +33,11 @@ class RegionLoader{
|
|||||||
const VERSION = 1;
|
const VERSION = 1;
|
||||||
const COMPRESSION_GZIP = 1;
|
const COMPRESSION_GZIP = 1;
|
||||||
const COMPRESSION_ZLIB = 2;
|
const COMPRESSION_ZLIB = 2;
|
||||||
|
|
||||||
const MAX_SECTOR_LENGTH = 256 << 12; //256 sectors, (1 MiB)
|
const MAX_SECTOR_LENGTH = 256 << 12; //256 sectors, (1 MiB)
|
||||||
|
const REGION_HEADER_LENGTH = 8192; //4096 location table + 4096 timestamps
|
||||||
|
const MAX_REGION_FILE_SIZE = 32 * 32 * self::MAX_SECTOR_LENGTH + self::REGION_HEADER_LENGTH; //32 * 32 1MiB chunks + header size
|
||||||
|
|
||||||
public static $COMPRESSION_LEVEL = 7;
|
public static $COMPRESSION_LEVEL = 7;
|
||||||
|
|
||||||
protected $x;
|
protected $x;
|
||||||
@ -51,10 +56,21 @@ class RegionLoader{
|
|||||||
$this->z = $regionZ;
|
$this->z = $regionZ;
|
||||||
$this->levelProvider = $level;
|
$this->levelProvider = $level;
|
||||||
$this->filePath = $this->levelProvider->getPath() . "region/r.$regionX.$regionZ.$fileExtension";
|
$this->filePath = $this->levelProvider->getPath() . "region/r.$regionX.$regionZ.$fileExtension";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function open(){
|
||||||
$exists = file_exists($this->filePath);
|
$exists = file_exists($this->filePath);
|
||||||
if(!$exists){
|
if(!$exists){
|
||||||
touch($this->filePath);
|
touch($this->filePath);
|
||||||
|
}else{
|
||||||
|
$fileSize = filesize($this->filePath);
|
||||||
|
if($fileSize > self::MAX_REGION_FILE_SIZE){
|
||||||
|
throw new CorruptedRegionException("Corrupted oversized region file found, should be a maximum of " . self::MAX_REGION_FILE_SIZE . " bytes, got " . $fileSize . " bytes");
|
||||||
|
}elseif($fileSize % 4096 !== 0){
|
||||||
|
throw new CorruptedRegionException("Region file should be padded to a multiple of 4KiB");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->filePointer = fopen($this->filePath, "r+b");
|
$this->filePointer = fopen($this->filePath, "r+b");
|
||||||
stream_set_read_buffer($this->filePointer, 1024 * 16); //16KB
|
stream_set_read_buffer($this->filePointer, 1024 * 16); //16KB
|
||||||
stream_set_write_buffer($this->filePointer, 1024 * 16); //16KB
|
stream_set_write_buffer($this->filePointer, 1024 * 16); //16KB
|
||||||
@ -170,9 +186,20 @@ class RegionLoader{
|
|||||||
return $x + ($z << 5);
|
return $x + ($z << 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function close(){
|
/**
|
||||||
|
* Writes the region header and closes the file
|
||||||
|
*
|
||||||
|
* @param bool $writeHeader
|
||||||
|
*/
|
||||||
|
public function close(bool $writeHeader = true){
|
||||||
|
if(is_resource($this->filePointer)){
|
||||||
|
if($writeHeader){
|
||||||
$this->writeLocationTable();
|
$this->writeLocationTable();
|
||||||
|
}
|
||||||
|
|
||||||
fclose($this->filePointer);
|
fclose($this->filePointer);
|
||||||
|
}
|
||||||
|
|
||||||
$this->levelProvider = null;
|
$this->levelProvider = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,14 +282,34 @@ class RegionLoader{
|
|||||||
fseek($this->filePointer, 0);
|
fseek($this->filePointer, 0);
|
||||||
$this->lastSector = 1;
|
$this->lastSector = 1;
|
||||||
|
|
||||||
$data = unpack("N*", fread($this->filePointer, 4 * 1024 * 2)); //1024 records * 4 bytes * 2 times
|
$headerRaw = fread($this->filePointer, self::REGION_HEADER_LENGTH);
|
||||||
|
if(($len = strlen($headerRaw)) !== self::REGION_HEADER_LENGTH){
|
||||||
|
throw new CorruptedRegionException("Invalid region file header, expected " . self::REGION_HEADER_LENGTH . " bytes, got " . $len . " bytes");
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = unpack("N*", $headerRaw);
|
||||||
|
$usedOffsets = [];
|
||||||
for($i = 0; $i < 1024; ++$i){
|
for($i = 0; $i < 1024; ++$i){
|
||||||
$index = $data[$i + 1];
|
$index = $data[$i + 1];
|
||||||
|
$offset = $index >> 8;
|
||||||
|
if($offset !== 0){
|
||||||
|
fseek($this->filePointer, ($offset << 12));
|
||||||
|
if(fgetc($this->filePointer) === false){ //Try and read from the location
|
||||||
|
throw new CorruptedRegionException("Region file location offset points to invalid location");
|
||||||
|
}elseif(isset($usedOffsets[$offset])){
|
||||||
|
throw new CorruptedRegionException("Found two chunk offsets pointing to the same location");
|
||||||
|
}else{
|
||||||
|
$usedOffsets[$offset] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->locationTable[$i] = [$index >> 8, $index & 0xff, $data[1024 + $i + 1]];
|
$this->locationTable[$i] = [$index >> 8, $index & 0xff, $data[1024 + $i + 1]];
|
||||||
if(($this->locationTable[$i][0] + $this->locationTable[$i][1] - 1) > $this->lastSector){
|
if(($this->locationTable[$i][0] + $this->locationTable[$i][1] - 1) > $this->lastSector){
|
||||||
$this->lastSector = $this->locationTable[$i][0] + $this->locationTable[$i][1] - 1;
|
$this->lastSector = $this->locationTable[$i][0] + $this->locationTable[$i][1] - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fseek($this->filePointer, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function writeLocationTable(){
|
private function writeLocationTable(){
|
||||||
@ -300,4 +347,7 @@ class RegionLoader{
|
|||||||
return $this->z;
|
return $this->z;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getFilePath() : string{
|
||||||
|
return $this->filePath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -292,6 +292,4 @@ interface NetworkSession{
|
|||||||
public function handleStopSound(StopSoundPacket $packet) : bool;
|
public function handleStopSound(StopSoundPacket $packet) : bool;
|
||||||
|
|
||||||
public function handleSetTitle(SetTitlePacket $packet) : bool;
|
public function handleSetTitle(SetTitlePacket $packet) : bool;
|
||||||
|
|
||||||
public function handleUnknown(UnknownPacket $packet) : bool;
|
|
||||||
}
|
}
|
@ -40,6 +40,10 @@ abstract class DataPacket extends BinaryStream{
|
|||||||
return $this::NETWORK_ID;
|
return $this::NETWORK_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getName() : string{
|
||||||
|
return (new \ReflectionClass($this))->getShortName();
|
||||||
|
}
|
||||||
|
|
||||||
public function canBeBatched() : bool{
|
public function canBeBatched() : bool{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -52,6 +56,16 @@ abstract class DataPacket extends BinaryStream{
|
|||||||
|
|
||||||
abstract public function decode();
|
abstract public function decode();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs handling for this packet. Usually you'll want an appropriately named method in the NetworkSession for this.
|
||||||
|
*
|
||||||
|
* This method returns a bool to indicate whether the packet was handled or not. If the packet was unhandled, a debug message will be logged with a hexdump of the packet.
|
||||||
|
* Typically this method returns the return value of the handler in the supplied NetworkSession. See other packets for examples how to implement this.
|
||||||
|
*
|
||||||
|
* @param NetworkSession $session
|
||||||
|
*
|
||||||
|
* @return bool true if the packet was handled successfully, false if not.
|
||||||
|
*/
|
||||||
abstract public function handle(NetworkSession $session) : bool;
|
abstract public function handle(NetworkSession $session) : bool;
|
||||||
|
|
||||||
public function reset(){
|
public function reset(){
|
||||||
|
@ -37,6 +37,10 @@ class UnknownPacket extends DataPacket{
|
|||||||
return self::NETWORK_ID;
|
return self::NETWORK_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getName() : string{
|
||||||
|
return "unknown packet";
|
||||||
|
}
|
||||||
|
|
||||||
public function decode(){
|
public function decode(){
|
||||||
$this->offset -= 1; //Rewind one byte so we can read the PID
|
$this->offset -= 1; //Rewind one byte so we can read the PID
|
||||||
$this->payload = $this->get(true);
|
$this->payload = $this->get(true);
|
||||||
@ -48,6 +52,6 @@ class UnknownPacket extends DataPacket{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function handle(NetworkSession $session) : bool{
|
public function handle(NetworkSession $session) : bool{
|
||||||
return $session->handleUnknown($this);
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user