Fixed remaining cases of undefined behaviour on ambiguous world format

It was still possible to produce undefined behaviour by creating a db folder in a region-based world, or adding regions to a leveldb world. This now solves the problem completely by refusing to load the world if multiple formats match the world.
This commit is contained in:
Dylan K. Taylor 2018-10-04 19:43:31 +01:00
parent 0cc4bc48cc
commit a273a0c8a9
4 changed files with 23 additions and 26 deletions

View File

@ -1035,13 +1035,17 @@ class Server{
$path = $this->getDataPath() . "worlds/" . $name . "/"; $path = $this->getDataPath() . "worlds/" . $name . "/";
$providerClass = LevelProviderManager::getProvider($path); $providers = LevelProviderManager::getMatchingProviders($path);
if(count($providers) !== 1){
if($providerClass === null){ $this->logger->error($this->language->translateString("pocketmine.level.loadError", [
$this->logger->error($this->getLanguage()->translateString("pocketmine.level.loadError", [$name, "Cannot identify format of world"])); $name,
empty($providers) ?
$this->language->translateString("pocketmine.level.unknownFormat") :
$this->language->translateString("pocketmine.level.ambiguousFormat", [implode(", ", array_keys($providers))])
]));
return false; return false;
} }
$providerClass = array_shift($providers);
/** @see LevelProvider::__construct() */ /** @see LevelProvider::__construct() */
$level = new Level($this, $name, new $providerClass($path)); $level = new Level($this, $name, new $providerClass($path));

@ -1 +1 @@
Subproject commit 7f12f293bfad76e5c07b929c893e848728064977 Subproject commit e738dc7ca0171a1a16e3682fc2a7619c059988b9

View File

@ -87,17 +87,17 @@ abstract class LevelProviderManager{
* *
* @param string $path * @param string $path
* *
* @return string|null * @return string[]|LevelProvider[]
*/ */
public static function getProvider(string $path){ public static function getMatchingProviders(string $path) : array{
foreach(self::$providers as $provider){ $result = [];
/** @var $provider LevelProvider */ foreach(self::$providers as $alias => $provider){
/** @var LevelProvider|string $provider */
if($provider::isValid($path)){ if($provider::isValid($path)){
return $provider; $result[$alias] = $provider;
} }
} }
return $result;
return null;
} }
/** /**

View File

@ -45,23 +45,16 @@ abstract class RegionLevelProvider extends BaseLevelProvider{
abstract protected static function getPcWorldFormatVersion() : int; abstract protected static function getPcWorldFormatVersion() : int;
public static function isValid(string $path) : bool{ public static function isValid(string $path) : bool{
$isValid = (file_exists($path . "/level.dat") and is_dir($path . "/region/")); if(file_exists($path . "/level.dat") and is_dir($path . "/region/")){
foreach(scandir($path . "/region/", SCANDIR_SORT_NONE) as $file){
if($isValid){ if(substr($file, strrpos($file, ".") + 1) === static::getRegionFileExtension()){
$files = array_filter(scandir($path . "/region/", SCANDIR_SORT_NONE), function($file){ //we don't care if other region types exist, we only care if this format is possible
return substr($file, strrpos($file, ".") + 1, 2) === "mc"; //region file return true;
});
$ext = static::getRegionFileExtension();
foreach($files as $f){
if(substr($f, strrpos($f, ".") + 1) !== $ext){
$isValid = false;
break;
} }
} }
} }
return $isValid; return false;
} }
public static function generate(string $path, string $name, int $seed, string $generator, array $options = []){ public static function generate(string $path, string $name, int $seed, string $generator, array $options = []){