mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-23 00:55:57 +00:00
Add PMCustomAnvil format
Exactly the same as Anvil, but with XZY data ordering. Significantly more performant than Anvil due to no re-ordering necessary.
This commit is contained in:
parent
1eaf7c5431
commit
f180167955
@ -63,6 +63,7 @@ use pocketmine\level\format\leveldb\LevelDB;
|
||||
use pocketmine\level\format\LevelProviderManager;
|
||||
use pocketmine\level\format\region\Anvil;
|
||||
use pocketmine\level\format\region\McRegion;
|
||||
use pocketmine\level\format\region\PMCustomAnvil;
|
||||
use pocketmine\level\generator\biome\Biome;
|
||||
use pocketmine\level\generator\Flat;
|
||||
use pocketmine\level\generator\Generator;
|
||||
@ -1524,11 +1525,12 @@ class Server{
|
||||
|
||||
$this->enablePlugins(PluginLoadOrder::STARTUP);
|
||||
|
||||
LevelProviderManager::addProvider($this, Anvil::class);
|
||||
LevelProviderManager::addProvider($this, McRegion::class);
|
||||
LevelProviderManager::addProvider(Anvil::class);
|
||||
LevelProviderManager::addProvider(McRegion::class);
|
||||
LevelProviderManager::addProvider(PMCustomAnvil::class);
|
||||
if(extension_loaded("leveldb")){
|
||||
$this->logger->debug($this->getLanguage()->translateString("pocketmine.debug.enable"));
|
||||
LevelProviderManager::addProvider($this, LevelDB::class);
|
||||
LevelProviderManager::addProvider(LevelDB::class);
|
||||
}
|
||||
|
||||
|
||||
|
@ -19,21 +19,21 @@
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace pocketmine\level\format;
|
||||
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\LevelException;
|
||||
|
||||
abstract class LevelProviderManager{
|
||||
protected static $providers = [];
|
||||
|
||||
/**
|
||||
* @param Server $server
|
||||
* @param string $class
|
||||
*
|
||||
* @throws LevelException
|
||||
*/
|
||||
public static function addProvider(Server $server, $class){
|
||||
public static function addProvider(string $class){
|
||||
if(!is_subclass_of($class, LevelProvider::class)){
|
||||
throw new LevelException("Class is not a subclass of LevelProvider");
|
||||
}
|
||||
@ -46,9 +46,9 @@ abstract class LevelProviderManager{
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public static function getProvider($path){
|
||||
public static function getProvider(string $path){
|
||||
foreach(self::$providers as $provider){
|
||||
/** @var $provider LevelProvider */
|
||||
if($provider::isValid($path)){
|
||||
@ -59,9 +59,14 @@ abstract class LevelProviderManager{
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function getProviderByName($name){
|
||||
$name = trim(strtolower($name));
|
||||
|
||||
return self::$providers[$name] ?? null;
|
||||
/**
|
||||
* Returns a LevelProvider by name, or null if not found
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public static function getProviderByName(string $name){
|
||||
return self::$providers[trim(strtolower($name))] ?? null;
|
||||
}
|
||||
}
|
158
src/pocketmine/level/format/region/PMCustomAnvil.php
Normal file
158
src/pocketmine/level/format/region/PMCustomAnvil.php
Normal file
@ -0,0 +1,158 @@
|
||||
<?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\region;
|
||||
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
use pocketmine\level\format\generic\GenericChunk;
|
||||
use pocketmine\level\format\generic\SubChunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\tag\{ByteArrayTag, ByteTag, CompoundTag, IntArrayTag, IntTag, ListTag, LongTag};
|
||||
use pocketmine\Player;
|
||||
use pocketmine\tile\Spawnable;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
use pocketmine\utils\ChunkException;
|
||||
use pocketmine\utils\MainLogger;
|
||||
|
||||
/**
|
||||
* This format is exactly the same as the PC Anvil format, with the only difference being that the stored data order is XZY instead of YZX for more performance loading and
|
||||
* saving worlds.
|
||||
*/
|
||||
class PMCustomAnvil extends Anvil{
|
||||
|
||||
const REGION_FILE_EXTENSION = "mcapm";
|
||||
|
||||
public function nbtSerialize(GenericChunk $chunk) : string{
|
||||
$nbt = new CompoundTag("Level", []);
|
||||
$nbt->xPos = new IntTag("xPos", $chunk->getX());
|
||||
$nbt->zPos = new IntTag("zPos", $chunk->getZ());
|
||||
|
||||
$nbt->V = new ByteTag("V", 1);
|
||||
$nbt->LastUpdate = new LongTag("LastUpdate", 0); //TODO
|
||||
$nbt->InhabitedTime = new LongTag("InhabitedTime", 0); //TODO
|
||||
$nbt->TerrainPopulated = new ByteTag("TerrainPopulated", $chunk->isPopulated());
|
||||
$nbt->LightPopulated = new ByteTag("LightPopulated", $chunk->isLightPopulated());
|
||||
|
||||
$nbt->Sections = new ListTag("Sections", []);
|
||||
$nbt->Sections->setTagType(NBT::TAG_Compound);
|
||||
$subChunks = -1;
|
||||
foreach($chunk->getSubChunks() as $subChunk){
|
||||
if($subChunk->isEmpty()){
|
||||
continue;
|
||||
}
|
||||
$nbt->Sections[++$subChunks] = new CompoundTag(null, [
|
||||
"Y" => new ByteTag("Y", $subChunk->getY()),
|
||||
"Blocks" => new ByteArrayTag("Blocks", $subChunk->getBlockIdArray()),
|
||||
"Data" => new ByteArrayTag("Data", $subChunk->getBlockDataArray()),
|
||||
"BlockLight" => new ByteArrayTag("BlockLight", $subChunk->getBlockLightArray()),
|
||||
"SkyLight" => new ByteArrayTag("SkyLight", $subChunk->getSkyLightArray())
|
||||
]);
|
||||
}
|
||||
|
||||
$nbt->Biomes = new ByteArrayTag("Biomes", $chunk->getBiomeIdArray());
|
||||
$nbt->HeightMap = new IntArrayTag("HeightMap", $chunk->getHeightMapArray());
|
||||
|
||||
$entities = [];
|
||||
|
||||
foreach($chunk->getEntities() as $entity){
|
||||
if(!($entity instanceof Player) and !$entity->closed){
|
||||
$entity->saveNBT();
|
||||
$entities[] = $entity->namedtag;
|
||||
}
|
||||
}
|
||||
|
||||
$nbt->Entities = new ListTag("Entities", $entities);
|
||||
$nbt->Entities->setTagType(NBT::TAG_Compound);
|
||||
|
||||
$tiles = [];
|
||||
foreach($chunk->getTiles() as $tile){
|
||||
$tile->saveNBT();
|
||||
$tiles[] = $tile->namedtag;
|
||||
}
|
||||
|
||||
$nbt->TileEntities = new ListTag("TileEntities", $tiles);
|
||||
$nbt->TileEntities->setTagType(NBT::TAG_Compound);
|
||||
|
||||
//TODO: TileTicks
|
||||
|
||||
$writer = new NBT(NBT::BIG_ENDIAN);
|
||||
$nbt->setName("Level");
|
||||
$writer->setData(new CompoundTag("", ["Level" => $nbt]));
|
||||
|
||||
return $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL);
|
||||
}
|
||||
|
||||
public function nbtDeserialize(string $data){
|
||||
$nbt = new NBT(NBT::BIG_ENDIAN);
|
||||
try{
|
||||
$nbt->readCompressed($data, ZLIB_ENCODING_DEFLATE);
|
||||
|
||||
$chunk = $nbt->getData();
|
||||
|
||||
if(!isset($chunk->Level) or !($chunk->Level instanceof CompoundTag)){
|
||||
throw new ChunkException("Invalid NBT format");
|
||||
}
|
||||
|
||||
$chunk = $chunk->Level;
|
||||
|
||||
$subChunks = [];
|
||||
if($chunk->Sections instanceof ListTag){
|
||||
foreach($chunk->Sections as $subChunk){
|
||||
if($subChunk instanceof CompoundTag){
|
||||
$subChunks[] = new SubChunk(
|
||||
$subChunk->Y->getValue(),
|
||||
$subChunk->Blocks->getValue(),
|
||||
$subChunk->Data->getValue(),
|
||||
$subChunk->BlockLight->getValue(),
|
||||
$subChunk->SkyLight->getValue()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result = new GenericChunk(
|
||||
$this,
|
||||
$chunk["xPos"],
|
||||
$chunk["zPos"],
|
||||
$subChunks,
|
||||
$chunk->Entities->getValue(),
|
||||
$chunk->TileEntities->getValue(),
|
||||
$chunk->Biomes->getValue(),
|
||||
$chunk->HeightMap->getValue()
|
||||
);
|
||||
$result->setLightPopulated(isset($chunk->LightPopulated) ? ((bool) $chunk->LightPopulated->getValue()) : false);
|
||||
$result->setPopulated(isset($chunk->TerrainPopulated) ? ((bool) $chunk->TerrainPopulated->getValue()) : false);
|
||||
$result->setGenerated(true);
|
||||
return $result;
|
||||
}catch(\Throwable $e){
|
||||
MainLogger::getLogger()->logException($e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static function getProviderName() : string{
|
||||
return "pmcustomanvil";
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user