mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-07 04:17:07 +00:00
New generic in-memory chunk format, fixed 0.17.0.1 chunk loading
Terrible performance, needs profiling. TODO: fix this.
This commit is contained in:
parent
60260a294b
commit
4c49db6036
@ -81,7 +81,7 @@ use pocketmine\inventory\ShapelessRecipe;
|
||||
use pocketmine\inventory\SimpleTransactionGroup;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\ChunkLoader;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\level\Location;
|
||||
use pocketmine\level\Position;
|
||||
@ -3540,17 +3540,17 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade
|
||||
$this->server->getPlayerMetadata()->removeMetadata($this, $metadataKey, $plugin);
|
||||
}
|
||||
|
||||
public function onChunkChanged(FullChunk $chunk){
|
||||
public function onChunkChanged(Chunk $chunk){
|
||||
$this->loadQueue[Level::chunkHash($chunk->getX(), $chunk->getZ())] = abs(($this->x >> 4) - $chunk->getX()) + abs(($this->z >> 4) - $chunk->getZ());
|
||||
}
|
||||
|
||||
public function onChunkLoaded(FullChunk $chunk){
|
||||
public function onChunkLoaded(Chunk $chunk){
|
||||
}
|
||||
|
||||
public function onChunkPopulated(FullChunk $chunk){
|
||||
public function onChunkPopulated(Chunk $chunk){
|
||||
}
|
||||
|
||||
public function onChunkUnloaded(FullChunk $chunk){
|
||||
public function onChunkUnloaded(Chunk $chunk){
|
||||
}
|
||||
|
||||
public function onBlockChanged(Vector3 $block){
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
namespace pocketmine\entity;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\particle\CriticalParticle;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\network\protocol\AddEntityPacket;
|
||||
@ -41,7 +41,7 @@ class Arrow extends Projectile{
|
||||
|
||||
protected $isCritical;
|
||||
|
||||
public function __construct(FullChunk $chunk, CompoundTag $nbt, Entity $shootingEntity = null, $critical = false){
|
||||
public function __construct(Chunk $chunk, CompoundTag $nbt, Entity $shootingEntity = null, $critical = false){
|
||||
$this->isCritical = (bool) $critical;
|
||||
parent::__construct($chunk, $nbt, $shootingEntity);
|
||||
}
|
||||
|
@ -35,7 +35,6 @@ use pocketmine\event\entity\EntitySpawnEvent;
|
||||
use pocketmine\event\entity\EntityTeleportEvent;
|
||||
use pocketmine\event\Timings;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\level\Location;
|
||||
use pocketmine\level\Position;
|
||||
@ -255,7 +254,7 @@ abstract class Entity extends Location implements Metadatable{
|
||||
protected $isPlayer = false;
|
||||
|
||||
|
||||
public function __construct(FullChunk $chunk, CompoundTag $nbt){
|
||||
public function __construct(Chunk $chunk, CompoundTag $nbt){
|
||||
|
||||
assert($chunk !== null and $chunk->getProvider() !== null);
|
||||
|
||||
@ -487,13 +486,13 @@ abstract class Entity extends Location implements Metadatable{
|
||||
|
||||
/**
|
||||
* @param int|string $type
|
||||
* @param FullChunk $chunk
|
||||
* @param Chunk $chunk
|
||||
* @param CompoundTag $nbt
|
||||
* @param $args
|
||||
*
|
||||
* @return Entity
|
||||
*/
|
||||
public static function createEntity($type, FullChunk $chunk, CompoundTag $nbt, ...$args){
|
||||
public static function createEntity($type, Chunk $chunk, CompoundTag $nbt, ...$args){
|
||||
if(isset(self::$knownEntities[$type])){
|
||||
$class = self::$knownEntities[$type];
|
||||
return new $class($chunk, $nbt, ...$args);
|
||||
|
@ -26,7 +26,7 @@ use pocketmine\event\entity\EntityDamageByChildEntityEvent;
|
||||
use pocketmine\event\entity\EntityDamageByEntityEvent;
|
||||
use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\event\entity\ProjectileHitEvent;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\MovingObjectPosition;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
@ -42,7 +42,7 @@ abstract class Projectile extends Entity{
|
||||
|
||||
public $hadCollision = false;
|
||||
|
||||
public function __construct(FullChunk $chunk, CompoundTag $nbt, Entity $shootingEntity = null){
|
||||
public function __construct(Chunk $chunk, CompoundTag $nbt, Entity $shootingEntity = null){
|
||||
$this->shootingEntity = $shootingEntity;
|
||||
if($shootingEntity !== null){
|
||||
$this->setDataProperty(self::DATA_SHOOTER_ID, self::DATA_TYPE_LONG, $shootingEntity->getId());
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
namespace pocketmine\entity;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\network\protocol\AddEntityPacket;
|
||||
use pocketmine\Player;
|
||||
@ -36,7 +36,7 @@ class Snowball extends Projectile{
|
||||
protected $gravity = 0.03;
|
||||
protected $drag = 0.01;
|
||||
|
||||
public function __construct(FullChunk $chunk, CompoundTag $nbt, Entity $shootingEntity = null){
|
||||
public function __construct(Chunk $chunk, CompoundTag $nbt, Entity $shootingEntity = null){
|
||||
parent::__construct($chunk, $nbt, $shootingEntity);
|
||||
}
|
||||
|
||||
|
@ -24,22 +24,22 @@
|
||||
*/
|
||||
namespace pocketmine\event\level;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\Chunk;
|
||||
|
||||
abstract class ChunkEvent extends LevelEvent{
|
||||
/** @var FullChunk */
|
||||
/** @var Chunk */
|
||||
private $chunk;
|
||||
|
||||
/**
|
||||
* @param FullChunk $chunk
|
||||
* @param Chunk $chunk
|
||||
*/
|
||||
public function __construct(FullChunk $chunk){
|
||||
public function __construct(Chunk $chunk){
|
||||
parent::__construct($chunk->getProvider()->getLevel());
|
||||
$this->chunk = $chunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FullChunk
|
||||
* @return Chunk
|
||||
*/
|
||||
public function getChunk(){
|
||||
return $this->chunk;
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
namespace pocketmine\event\level;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\Chunk;
|
||||
|
||||
/**
|
||||
* Called when a Chunk is loaded
|
||||
@ -31,7 +31,7 @@ class ChunkLoadEvent extends ChunkEvent{
|
||||
|
||||
private $newChunk;
|
||||
|
||||
public function __construct(FullChunk $chunk, $newChunk){
|
||||
public function __construct(Chunk $chunk, $newChunk){
|
||||
parent::__construct($chunk);
|
||||
$this->newChunk = (bool) $newChunk;
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ namespace pocketmine\item;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\DoubleTag;
|
||||
@ -46,7 +46,7 @@ class SpawnEgg extends Item{
|
||||
$entity = null;
|
||||
$chunk = $level->getChunk($block->getX() >> 4, $block->getZ() >> 4);
|
||||
|
||||
if(!($chunk instanceof FullChunk)){
|
||||
if(!($chunk instanceof Chunk)){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
namespace pocketmine\level;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\math\Vector3;
|
||||
|
||||
/**
|
||||
@ -75,32 +75,32 @@ interface ChunkLoader{
|
||||
/**
|
||||
* This method will be called when a Chunk is replaced by a new one
|
||||
*
|
||||
* @param FullChunk $chunk
|
||||
* @param Chunk $chunk
|
||||
*/
|
||||
public function onChunkChanged(FullChunk $chunk);
|
||||
public function onChunkChanged(Chunk $chunk);
|
||||
|
||||
/**
|
||||
* This method will be called when a registered chunk is loaded
|
||||
*
|
||||
* @param FullChunk $chunk
|
||||
* @param Chunk $chunk
|
||||
*/
|
||||
public function onChunkLoaded(FullChunk $chunk);
|
||||
public function onChunkLoaded(Chunk $chunk);
|
||||
|
||||
|
||||
/**
|
||||
* This method will be called when a registered chunk is unloaded
|
||||
*
|
||||
* @param FullChunk $chunk
|
||||
* @param Chunk $chunk
|
||||
*/
|
||||
public function onChunkUnloaded(FullChunk $chunk);
|
||||
public function onChunkUnloaded(Chunk $chunk);
|
||||
|
||||
/**
|
||||
* This method will be called when a registered chunk is populated
|
||||
* Usually it'll be sent with another call to onChunkChanged()
|
||||
*
|
||||
* @param FullChunk $chunk
|
||||
* @param Chunk $chunk
|
||||
*/
|
||||
public function onChunkPopulated(FullChunk $chunk);
|
||||
public function onChunkPopulated(Chunk $chunk);
|
||||
|
||||
/**
|
||||
* This method will be called when a block changes in a registered chunk
|
||||
|
@ -23,7 +23,7 @@ declare(strict_types = 1);
|
||||
|
||||
namespace pocketmine\level;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\Chunk;
|
||||
|
||||
interface ChunkManager{
|
||||
/**
|
||||
@ -72,16 +72,16 @@ interface ChunkManager{
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
*
|
||||
* @return FullChunk|null
|
||||
* @return Chunk|null
|
||||
*/
|
||||
public function getChunk(int $chunkX, int $chunkZ);
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param FullChunk $chunk
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param Chunk $chunk
|
||||
*/
|
||||
public function setChunk(int $chunkX, int $chunkZ, FullChunk $chunk = null);
|
||||
public function setChunk(int $chunkX, int $chunkZ, Chunk $chunk = null);
|
||||
|
||||
/**
|
||||
* Gets the level seed
|
||||
|
@ -66,7 +66,6 @@ use pocketmine\event\Timings;
|
||||
use pocketmine\inventory\InventoryHolder;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\generic\BaseLevelProvider;
|
||||
use pocketmine\level\format\generic\EmptyChunkSection;
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
@ -184,7 +183,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
private $folderName;
|
||||
|
||||
/** @var FullChunk[]|Chunk[] */
|
||||
/** @var Chunk[] */
|
||||
private $chunks = [];
|
||||
|
||||
/** @var Vector3[][] */
|
||||
@ -209,7 +208,6 @@ class Level implements ChunkManager, Metadatable{
|
||||
/** @var BlockMetadataStore */
|
||||
private $blockMetadata;
|
||||
|
||||
private $useSections;
|
||||
private $blockOrder;
|
||||
|
||||
/** @var Position */
|
||||
@ -334,7 +332,6 @@ class Level implements ChunkManager, Metadatable{
|
||||
$this->generator = Generator::getGenerator($this->provider->getGenerator());
|
||||
|
||||
$this->blockOrder = $provider::getProviderOrder();
|
||||
$this->useSections = $provider::usesChunkSection();
|
||||
|
||||
$this->folderName = $name;
|
||||
$this->updateQueue = new ReversePriorityQueue();
|
||||
@ -918,44 +915,20 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
|
||||
|
||||
if($this->useSections){
|
||||
foreach($chunk->getSections() as $section){
|
||||
if(!($section instanceof EmptyChunkSection)){
|
||||
$Y = $section->getY();
|
||||
$k = mt_rand(0, 0x7fffffff);
|
||||
for($i = 0; $i < 3; ++$i, $k >>= 10){
|
||||
$x = $k & 0x0f;
|
||||
$y = ($k >> 8) & 0x0f;
|
||||
$z = ($k >> 16) & 0x0f;
|
||||
|
||||
$blockId = $section->getBlockId($x, $y, $z);
|
||||
if(isset($this->randomTickBlocks[$blockId])){
|
||||
$class = $this->randomTickBlocks[$blockId];
|
||||
/** @var Block $block */
|
||||
$block = new $class($section->getBlockData($x, $y, $z));
|
||||
$block->x = $chunkX * 16 + $x;
|
||||
$block->y = ($Y << 4) + $y;
|
||||
$block->z = $chunkZ * 16 + $z;
|
||||
$block->level = $this;
|
||||
$block->onUpdate(self::BLOCK_UPDATE_RANDOM);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
for($Y = 0; $Y < 8 and ($Y < 3 or $blockTest !== 0); ++$Y){
|
||||
$blockTest = 0;
|
||||
foreach($chunk->getSubChunks() as $subChunk){
|
||||
if(!$subChunk->isEmpty()){
|
||||
$Y = $subChunk->getY();
|
||||
$k = mt_rand(0, 0x7fffffff);
|
||||
for($i = 0; $i < 3; ++$i, $k >>= 10){
|
||||
$x = $k & 0x0f;
|
||||
$y = ($k >> 8) & 0x0f;
|
||||
$z = ($k >> 16) & 0x0f;
|
||||
|
||||
$blockTest |= $blockId = $chunk->getBlockId($x, $y + ($Y << 4), $z);
|
||||
$blockId = $subChunk->getBlockId($x, $y, $z);
|
||||
if(isset($this->randomTickBlocks[$blockId])){
|
||||
$class = $this->randomTickBlocks[$blockId];
|
||||
/** @var Block $block */
|
||||
$block = new $class($chunk->getBlockData($x, $y + ($Y << 4), $z));
|
||||
$block = new $class($subChunk->getBlockData($x, $y, $z));
|
||||
$block->x = $chunkX * 16 + $x;
|
||||
$block->y = ($Y << 4) + $y;
|
||||
$block->z = $chunkZ * 16 + $z;
|
||||
@ -2122,7 +2095,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FullChunk[]|Chunk[]
|
||||
* @return Chunk[]
|
||||
*/
|
||||
public function getChunks() : array{
|
||||
return $this->chunks;
|
||||
@ -2135,7 +2108,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
* @param int $z
|
||||
* @param bool $create Whether to generate the chunk if it does not exist
|
||||
*
|
||||
* @return FullChunk|Chunk
|
||||
* @return Chunk
|
||||
*/
|
||||
public function getChunk(int $x, int $z, bool $create = false){
|
||||
if(isset($this->chunks[$index = Level::chunkHash($x, $z)])){
|
||||
@ -2147,7 +2120,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function generateChunkCallback(int $x, int $z, FullChunk $chunk){
|
||||
public function generateChunkCallback(int $x, int $z, Chunk $chunk){
|
||||
Timings::$generationCallbackTimer->startTiming();
|
||||
if(isset($this->chunkPopulationQueue[$index = Level::chunkHash($x, $z)])){
|
||||
$oldChunk = $this->getChunk($x, $z, false);
|
||||
@ -2182,10 +2155,10 @@ class Level implements ChunkManager, Metadatable{
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param FullChunk $chunk
|
||||
* @param Chunk $chunk
|
||||
* @param bool $unload
|
||||
*/
|
||||
public function setChunk(int $chunkX, int $chunkZ, FullChunk $chunk = null, bool $unload = true){
|
||||
public function setChunk(int $chunkX, int $chunkZ, Chunk $chunk = null, bool $unload = true){
|
||||
if($chunk === null){
|
||||
return;
|
||||
}
|
||||
|
@ -23,11 +23,11 @@ declare(strict_types = 1);
|
||||
|
||||
namespace pocketmine\level;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\Chunk;
|
||||
|
||||
class SimpleChunkManager implements ChunkManager{
|
||||
|
||||
/** @var FullChunk[] */
|
||||
/** @var Chunk[] */
|
||||
protected $chunks = [];
|
||||
|
||||
protected $seed;
|
||||
@ -100,7 +100,7 @@ class SimpleChunkManager implements ChunkManager{
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
*
|
||||
* @return FullChunk|null
|
||||
* @return Chunk|null
|
||||
*/
|
||||
public function getChunk(int $chunkX, int $chunkZ){
|
||||
return isset($this->chunks[$index = Level::chunkHash($chunkX, $chunkZ)]) ? $this->chunks[$index] : null;
|
||||
@ -109,9 +109,9 @@ class SimpleChunkManager implements ChunkManager{
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param FullChunk $chunk
|
||||
* @param Chunk $chunk
|
||||
*/
|
||||
public function setChunk(int $chunkX, int $chunkZ, FullChunk $chunk = null){
|
||||
public function setChunk(int $chunkX, int $chunkZ, Chunk $chunk = null){
|
||||
if($chunk === null){
|
||||
unset($this->chunks[Level::chunkHash($chunkX, $chunkZ)]);
|
||||
return;
|
||||
|
@ -21,36 +21,400 @@
|
||||
|
||||
namespace pocketmine\level\format;
|
||||
|
||||
interface Chunk extends FullChunk{
|
||||
const SECTION_COUNT = 8;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\level\format\generic\SubChunk;
|
||||
use pocketmine\tile\Tile;
|
||||
|
||||
interface Chunk{
|
||||
const MAX_SUBCHUNKS = 16;
|
||||
|
||||
const DATA_ORDER_XZY = 0;
|
||||
const DATA_ORDER_YZX = 1;
|
||||
|
||||
/**
|
||||
* Tests whether a section (mini-chunk) is empty
|
||||
* @return int
|
||||
*/
|
||||
public function getX() : int;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getZ() : int;
|
||||
|
||||
public function setX(int $x);
|
||||
|
||||
public function setZ(int $z);
|
||||
|
||||
/**
|
||||
* @return LevelProvider|null
|
||||
*/
|
||||
public function getProvider();
|
||||
|
||||
/**
|
||||
* @param LevelProvider $provider
|
||||
*/
|
||||
public function setProvider(LevelProvider $provider);
|
||||
|
||||
/**
|
||||
* Returns the chunk height in subchunks
|
||||
*
|
||||
* @param $fY 0-7, (Y / 16)
|
||||
* @return int
|
||||
*/
|
||||
public function getHeight() : int;
|
||||
/**
|
||||
* Gets block and meta in one go
|
||||
*
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-15
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int bitmap, (id << 4) | data
|
||||
*/
|
||||
public function getFullBlock(int $x, int $y, int $z) : int;
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-255
|
||||
* @param int $z 0-15
|
||||
* @param int $blockId , if null, do not change
|
||||
* @param int $meta 0-15, if null, do not change
|
||||
*
|
||||
*/
|
||||
public function setBlock(int $x, int $y, int $z, $blockId = null, $meta = null);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-255
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-255
|
||||
*/
|
||||
public function getBlockId(int $x, int $y, int $z) : int;
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-255
|
||||
* @param int $z 0-15
|
||||
* @param int $id 0-255
|
||||
*/
|
||||
public function setBlockId(int $x, int $y, int $z, int $id);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-255
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockData(int $x, int $y, int $z) : int;
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-255
|
||||
* @param int $z 0-15
|
||||
* @param int $data 0-15
|
||||
*/
|
||||
public function setBlockData(int $x, int $y, int $z, int $data);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-255
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int (16-bit)
|
||||
*/
|
||||
public function getBlockExtraData(int $x, int $y, int $z) : int;
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-255
|
||||
* @param int $z 0-15
|
||||
* @param int $data (16-bit)
|
||||
*/
|
||||
public function setBlockExtraData(int $x, int $y, int $z, int $data);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-255
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockSkyLight(int $x, int $y, int $z) : int;
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-255
|
||||
* @param int $z 0-15
|
||||
* @param int $level 0-15
|
||||
*/
|
||||
public function setBlockSkyLight(int $x, int $y, int $z, int $level);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-255
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockLight(int $x, int $y, int $z) : int;
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-255
|
||||
* @param int $z 0-15
|
||||
* @param int $level 0-15
|
||||
*/
|
||||
public function setBlockLight(int $x, int $y, int $z, int $level);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-255
|
||||
*/
|
||||
public function getHighestBlockAt(int $x, int $z) : int;
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-255
|
||||
*/
|
||||
public function getHeightMap(int $x, int $z) : int;
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $z 0-15
|
||||
* @param $value 0-255
|
||||
*/
|
||||
public function setHeightMap(int $x, int $z, int $value);
|
||||
|
||||
public function recalculateHeightMap();
|
||||
|
||||
public function populateSkyLight();
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-255
|
||||
*/
|
||||
public function getBiomeId(int $x, int $z) : int;
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $z 0-15
|
||||
* @param int $biomeId 0-255
|
||||
*/
|
||||
public function setBiomeId(int $x, int $z, int $biomeId);
|
||||
|
||||
/**
|
||||
* @param int $x
|
||||
* @param int $z
|
||||
*
|
||||
* @return int[] RGB bytes
|
||||
*/
|
||||
public function getBiomeColor(int $x, int $z) : int;
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $z 0-15
|
||||
* @param int $R 0-255
|
||||
* @param int $G 0-255
|
||||
* @param int $B 0-255
|
||||
*/
|
||||
public function setBiomeColor(int $x, int $z, int $R, int $G, int $B);
|
||||
|
||||
public function getBlockIdColumn(int $x, int $z) : string;
|
||||
|
||||
public function getBlockDataColumn(int $x, int $z) : string;
|
||||
|
||||
public function getBlockSkyLightColumn(int $x, int $z) : string;
|
||||
|
||||
public function getBlockLightColumn(int $x, int $z) : string;
|
||||
|
||||
public function isLightPopulated() : bool;
|
||||
|
||||
public function setLightPopulated(bool $value = true);
|
||||
|
||||
public function isPopulated() : bool;
|
||||
|
||||
public function setPopulated(bool $value = true);
|
||||
|
||||
public function isGenerated() : bool;
|
||||
|
||||
public function setGenerated(bool $value = true);
|
||||
|
||||
/**
|
||||
* @param Entity $entity
|
||||
*/
|
||||
public function addEntity(Entity $entity);
|
||||
|
||||
/**
|
||||
* @param Entity $entity
|
||||
*/
|
||||
public function removeEntity(Entity $entity);
|
||||
|
||||
/**
|
||||
* @param Tile $tile
|
||||
*/
|
||||
public function addTile(Tile $tile);
|
||||
|
||||
/**
|
||||
* @param Tile $tile
|
||||
*/
|
||||
public function removeTile(Tile $tile);
|
||||
|
||||
/**
|
||||
* @return Entity[]
|
||||
*/
|
||||
public function getEntities() : array;
|
||||
|
||||
/**
|
||||
* @return Tile[]
|
||||
*/
|
||||
public function getTiles() : array;
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-255
|
||||
* @param int $z 0-15
|
||||
*/
|
||||
public function getTile(int $x, int $y, int $z);
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isLoaded() : bool;
|
||||
|
||||
/**
|
||||
* Loads the chunk
|
||||
*
|
||||
* @param bool $generate If the chunk does not exist, generate it
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSectionEmpty($fY);
|
||||
public function load(bool $generate = true) : bool;
|
||||
|
||||
/**
|
||||
* @param int $fY 0-7
|
||||
* @param bool $save
|
||||
* @param bool $safe If false, unload the chunk even if players are nearby
|
||||
*
|
||||
* @return ChunkSection
|
||||
* @return bool
|
||||
*/
|
||||
public function getSection($fY);
|
||||
public function unload(bool $save = true, bool $safe = true) : bool;
|
||||
|
||||
public function initChunk();
|
||||
|
||||
/**
|
||||
* @param int $fY 0-7
|
||||
* @param ChunkSection $section
|
||||
* @return string
|
||||
*/
|
||||
public function getBiomeIdArray() : string;
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
public function getBiomeColorArray() : array;
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
public function getHeightMapArray() : array;
|
||||
|
||||
public function getBlockIdArray() : string;
|
||||
|
||||
public function getBlockDataArray() : string;
|
||||
|
||||
public function getBlockExtraDataArray() : array;
|
||||
|
||||
public function getBlockSkyLightArray() : string;
|
||||
|
||||
public function getBlockLightArray() : string;
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasChanged() : bool;
|
||||
|
||||
/**
|
||||
* @param bool $changed
|
||||
*/
|
||||
public function setChanged(bool $changed = true);
|
||||
|
||||
/**
|
||||
* @param int $fY 0-15
|
||||
* @param bool $generateNew will set and return a modifiable subchunk
|
||||
*
|
||||
* @return boolean
|
||||
* @return SubChunk
|
||||
*/
|
||||
public function setSection($fY, ChunkSection $section);
|
||||
public function getSubChunk(int $fY, bool $generateNew = false) : SubChunk;
|
||||
|
||||
/**
|
||||
* @param int $fY 0-15
|
||||
* @param SubChunk $subChunk
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function setSubChunk(int $fY, SubChunk $subChunk = null, bool $allowEmpty = false) : bool;
|
||||
|
||||
/**
|
||||
* @return ChunkSection[]
|
||||
* @return SubChunk[]
|
||||
*/
|
||||
public function getSections();
|
||||
public function getSubChunks() : array;
|
||||
|
||||
/**
|
||||
* Returns the index of the highest non-empty subchunk
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getHighestSubChunkIndex() : int;
|
||||
|
||||
/**
|
||||
* Returns the number of subchunks that need sending
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSubChunkSendCount() : int;
|
||||
|
||||
/**
|
||||
* Disposes of empty subchunks
|
||||
*/
|
||||
public function clearEmptySubChunks();
|
||||
|
||||
/**
|
||||
* Serializes the chunk to network data
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function networkSerialize() : string;
|
||||
|
||||
/**
|
||||
* Serializes a chunk without compression for use in AsyncTasks.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function fastSerialize(Chunk $chunk) : string;
|
||||
|
||||
/**
|
||||
* Deserializes a chunk from fast serialization
|
||||
*
|
||||
* @param string $data
|
||||
* @param LevelProvider $provider
|
||||
*
|
||||
* @return Chunk|null
|
||||
*/
|
||||
public static function fastDeserialize(string $data, LevelProvider $provider = null);
|
||||
|
||||
/**
|
||||
* Creates and returns an empty chunk
|
||||
*
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param LevelProvider $provider
|
||||
*
|
||||
* @return Chunk
|
||||
*/
|
||||
public static function getEmptyChunk(int $chunkX, int $chunkZ, LevelProvider $provider = null) : Chunk;
|
||||
|
||||
}
|
@ -1,169 +0,0 @@
|
||||
<?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;
|
||||
|
||||
interface ChunkSection{
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getY();
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-15
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-255
|
||||
*/
|
||||
public function getBlockId($x, $y, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-15
|
||||
* @param int $z 0-15
|
||||
* @param int $id 0-255
|
||||
*/
|
||||
public function setBlockId($x, $y, $z, $id);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-15
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockData($x, $y, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-15
|
||||
* @param int $z 0-15
|
||||
* @param int $data 0-15
|
||||
*/
|
||||
public function setBlockData($x, $y, $z, $data);
|
||||
|
||||
/**
|
||||
* Gets block and meta in one go
|
||||
*
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-15
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int bitmap, (id << 4) | data
|
||||
*/
|
||||
public function getFullBlock($x, $y, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-15
|
||||
* @param int $z 0-15
|
||||
* @param int $blockId , if null, do not change
|
||||
* @param int $meta 0-15, if null, do not change
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function setBlock($x, $y, $z, $blockId = null, $meta = null);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-15
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockSkyLight($x, $y, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-15
|
||||
* @param int $z 0-15
|
||||
* @param int $level 0-15
|
||||
*/
|
||||
public function setBlockSkyLight($x, $y, $z, $level);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-15
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockLight($x, $y, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-15
|
||||
* @param int $z 0-15
|
||||
* @param int $level 0-15
|
||||
*/
|
||||
public function setBlockLight($x, $y, $z, $level);
|
||||
|
||||
/**
|
||||
* Returns a id column from low y to high y
|
||||
*
|
||||
* @param int $x 0-15
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return string[16]
|
||||
*/
|
||||
public function getBlockIdColumn($x, $z);
|
||||
|
||||
/**
|
||||
* Returns a data column from low y to high y
|
||||
*
|
||||
* @param int $x 0-15
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return string[8]
|
||||
*/
|
||||
public function getBlockDataColumn($x, $z);
|
||||
|
||||
/**
|
||||
* Returns a skylight column from low y to high y
|
||||
*
|
||||
* @param int $x 0-15
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return string[8]
|
||||
*/
|
||||
public function getBlockSkyLightColumn($x, $z);
|
||||
|
||||
/**
|
||||
* Returns a data column from low y to high y
|
||||
*
|
||||
* @param int $x 0-15
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return string[8]
|
||||
*/
|
||||
public function getBlockLightColumn($x, $z);
|
||||
|
||||
public function getIdArray();
|
||||
|
||||
public function getDataArray();
|
||||
|
||||
public function getSkyLightArray();
|
||||
|
||||
public function getLightArray();
|
||||
|
||||
}
|
@ -1,363 +0,0 @@
|
||||
<?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;
|
||||
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\tile\Tile;
|
||||
|
||||
interface FullChunk{
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getX();
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getZ();
|
||||
|
||||
public function setX($x);
|
||||
|
||||
public function setZ($z);
|
||||
|
||||
/**
|
||||
* @return LevelProvider
|
||||
*/
|
||||
public function getProvider();
|
||||
|
||||
/**
|
||||
* @param LevelProvider $provider
|
||||
*/
|
||||
public function setProvider(LevelProvider $provider);
|
||||
|
||||
/**
|
||||
* Gets block and meta in one go
|
||||
*
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-15
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int bitmap, (id << 4) | data
|
||||
*/
|
||||
public function getFullBlock($x, $y, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
* @param int $blockId , if null, do not change
|
||||
* @param int $meta 0-15, if null, do not change
|
||||
*
|
||||
*/
|
||||
public function setBlock($x, $y, $z, $blockId = null, $meta = null);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-255
|
||||
*/
|
||||
public function getBlockId($x, $y, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
* @param int $id 0-255
|
||||
*/
|
||||
public function setBlockId($x, $y, $z, $id);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockData($x, $y, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
* @param int $data 0-15
|
||||
*/
|
||||
public function setBlockData($x, $y, $z, $data);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int (16-bit)
|
||||
*/
|
||||
public function getBlockExtraData($x, $y, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
* @param int $data (16-bit)
|
||||
*/
|
||||
public function setBlockExtraData($x, $y, $z, $data);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockSkyLight($x, $y, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
* @param int $level 0-15
|
||||
*/
|
||||
public function setBlockSkyLight($x, $y, $z, $level);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockLight($x, $y, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
* @param int $level 0-15
|
||||
*/
|
||||
public function setBlockLight($x, $y, $z, $level);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-127
|
||||
*/
|
||||
public function getHighestBlockAt($x, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-255
|
||||
*/
|
||||
public function getHeightMap($x, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $z 0-15
|
||||
* @param $value 0-255
|
||||
*/
|
||||
public function setHeightMap($x, $z, $value);
|
||||
|
||||
public function recalculateHeightMap();
|
||||
|
||||
public function populateSkyLight();
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $z 0-15
|
||||
*
|
||||
* @return int 0-255
|
||||
*/
|
||||
public function getBiomeId($x, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $z 0-15
|
||||
* @param int $biomeId 0-255
|
||||
*/
|
||||
public function setBiomeId($x, $z, $biomeId);
|
||||
|
||||
/**
|
||||
* @param int $x
|
||||
* @param int $z
|
||||
*
|
||||
* @return int[] RGB bytes
|
||||
*/
|
||||
public function getBiomeColor($x, $z);
|
||||
|
||||
public function getBlockIdColumn($x, $z);
|
||||
|
||||
public function getBlockDataColumn($x, $z);
|
||||
|
||||
public function getBlockSkyLightColumn($x, $z);
|
||||
|
||||
public function getBlockLightColumn($x, $z);
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $z 0-15
|
||||
* @param int $R 0-255
|
||||
* @param int $G 0-255
|
||||
* @param int $B 0-255
|
||||
*/
|
||||
public function setBiomeColor($x, $z, $R, $G, $B);
|
||||
|
||||
public function isLightPopulated();
|
||||
|
||||
public function setLightPopulated($value = 1);
|
||||
|
||||
public function isPopulated();
|
||||
|
||||
public function setPopulated($value = 1);
|
||||
|
||||
public function isGenerated();
|
||||
|
||||
public function setGenerated($value = 1);
|
||||
|
||||
/**
|
||||
* @param Entity $entity
|
||||
*/
|
||||
public function addEntity(Entity $entity);
|
||||
|
||||
/**
|
||||
* @param Entity $entity
|
||||
*/
|
||||
public function removeEntity(Entity $entity);
|
||||
|
||||
/**
|
||||
* @param Tile $tile
|
||||
*/
|
||||
public function addTile(Tile $tile);
|
||||
|
||||
/**
|
||||
* @param Tile $tile
|
||||
*/
|
||||
public function removeTile(Tile $tile);
|
||||
|
||||
/**
|
||||
* @return \pocketmine\entity\Entity[]
|
||||
*/
|
||||
public function getEntities();
|
||||
|
||||
/**
|
||||
* @return \pocketmine\tile\Tile[]
|
||||
*/
|
||||
public function getTiles();
|
||||
|
||||
/**
|
||||
* @param int $x 0-15
|
||||
* @param int $y 0-127
|
||||
* @param int $z 0-15
|
||||
*/
|
||||
public function getTile($x, $y, $z);
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isLoaded();
|
||||
|
||||
/**
|
||||
* Loads the chunk
|
||||
*
|
||||
* @param bool $generate If the chunk does not exist, generate it
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function load($generate = true);
|
||||
|
||||
/**
|
||||
* @param bool $save
|
||||
* @param bool $safe If false, unload the chunk even if players are nearby
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function unload($save = true, $safe = true);
|
||||
|
||||
public function initChunk();
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getBiomeIdArray();
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
public function getBiomeColorArray();
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
public function getHeightMapArray();
|
||||
|
||||
public function getBlockIdArray();
|
||||
|
||||
public function getBlockDataArray();
|
||||
|
||||
public function getBlockExtraDataArray();
|
||||
|
||||
public function getBlockSkyLightArray();
|
||||
|
||||
public function getBlockLightArray();
|
||||
|
||||
public function toBinary();
|
||||
|
||||
public function toFastBinary();
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasChanged();
|
||||
|
||||
/**
|
||||
* @param bool $changed
|
||||
*/
|
||||
public function setChanged($changed = true);
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
* @param LevelProvider $provider
|
||||
*
|
||||
* @return FullChunk
|
||||
*/
|
||||
public static function fromBinary($data, LevelProvider $provider = null);
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
* @param LevelProvider $provider
|
||||
*
|
||||
* @return FullChunk
|
||||
*/
|
||||
public static function fromFastBinary($data, LevelProvider $provider = null);
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param LevelProvider $provider
|
||||
*
|
||||
* @return FullChunk
|
||||
*/
|
||||
public static function getEmptyChunk($chunkX, $chunkZ, LevelProvider $provider = null);
|
||||
|
||||
}
|
@ -21,6 +21,7 @@
|
||||
|
||||
namespace pocketmine\level\format;
|
||||
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\math\Vector3;
|
||||
|
||||
@ -48,9 +49,11 @@ interface LevelProvider{
|
||||
public static function getProviderOrder();
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* Gets the build height limit of this world
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function usesChunkSection();
|
||||
public function getWorldHeight() : int;
|
||||
|
||||
/**
|
||||
* Requests a MC: PE network chunk to be sent
|
||||
@ -106,17 +109,10 @@ interface LevelProvider{
|
||||
* @param int $Z absolute Chunk Z value
|
||||
* @param bool $create Whether to generate the chunk if it does not exist
|
||||
*
|
||||
* @return FullChunk|Chunk
|
||||
* @return Chunk
|
||||
*/
|
||||
public function getChunk($X, $Z, $create = false);
|
||||
|
||||
/**
|
||||
* @param $Y 0-7
|
||||
*
|
||||
* @return ChunkSection
|
||||
*/
|
||||
public static function createChunkSection($Y);
|
||||
|
||||
public function saveChunks();
|
||||
|
||||
/**
|
||||
@ -172,11 +168,11 @@ interface LevelProvider{
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param FullChunk $chunk
|
||||
* @param Chunk $chunk
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function setChunk($chunkX, $chunkZ, FullChunk $chunk);
|
||||
public function setChunk($chunkX, $chunkZ, Chunk $chunk);
|
||||
|
||||
/**
|
||||
* @return string
|
||||
@ -214,7 +210,7 @@ interface LevelProvider{
|
||||
public function setSpawn(Vector3 $pos);
|
||||
|
||||
/**
|
||||
* @return FullChunk|Chunk[]
|
||||
* @return Chunk[]
|
||||
*/
|
||||
public function getLoadedChunks();
|
||||
|
||||
|
@ -21,24 +21,134 @@
|
||||
|
||||
namespace pocketmine\level\format\anvil;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
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\format\mcregion\McRegion;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\tag\ByteArrayTag;
|
||||
use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\network\protocol\FullChunkDataPacket;
|
||||
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;
|
||||
|
||||
class Anvil extends McRegion{
|
||||
|
||||
public static 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->TerrainGenerated = new ByteTag("TerrainGenerated", $chunk->isGenerated());
|
||||
$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", GenericChunk::reorderByteArray($subChunk->getBlockIdArray())), //Generic in-memory chunks are currrently always XZY
|
||||
"Data" => new ByteArrayTag("Data", GenericChunk::reorderNibbleArray($subChunk->getBlockDataArray())),
|
||||
"BlockLight" => new ByteArrayTag("BlockLight", GenericChunk::reorderNibbleArray($subChunk->getBlockLightArray())),
|
||||
"SkyLight" => new ByteArrayTag("SkyLight", GenericChunk::reorderNibbleArray($subChunk->getSkyLightArray()))
|
||||
]);
|
||||
}
|
||||
|
||||
$nbt->BiomeColors = new IntArrayTag("BiomeColors", $chunk->getBiomeColorArray());
|
||||
$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 static function nbtDeserialize(string $data, LevelProvider $provider = null){
|
||||
$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(),
|
||||
GenericChunk::reorderByteArray($subChunk->Blocks->getValue()),
|
||||
GenericChunk::reorderNibbleArray($subChunk->Data->getValue()),
|
||||
GenericChunk::reorderNibbleArray($subChunk->BlockLight->getValue()),
|
||||
GenericChunk::reorderNibbleArray($subChunk->SkyLight->getValue())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result = new GenericChunk(
|
||||
$provider,
|
||||
$chunk["xPos"],
|
||||
$chunk["zPos"],
|
||||
$subChunks,
|
||||
$chunk->Entities instanceof ListTag ? $chunk->Entities->getValue() : [],
|
||||
$chunk->TileEntities instanceof ListTag ? $chunk->TileEntities->getValue() : [],
|
||||
$chunk->BiomeColors instanceof IntArrayTag ? $chunk->BiomeColors->getValue() : [],
|
||||
$chunk->HeightMap instanceof IntArrayTag ? $chunk->HeightMap->getValue() : []
|
||||
);
|
||||
$result->setLightPopulated($chunk->LightPopulated instanceof ByteTag ? ((bool) $chunk->LightPopulated->getValue()) : false);
|
||||
$result->setPopulated($chunk->TerrainPopulated instanceof ByteTag ? ((bool) $chunk->TerrainPopulated->getValue()) : false);
|
||||
$result->setGenerated($chunk->TerrainGenerated instanceof ByteTag ? ((bool) $chunk->TerrainGenerated->getValue()) : false);
|
||||
return $result;
|
||||
}catch(\Throwable $e){
|
||||
echo $e->getMessage();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** @var RegionLoader[] */
|
||||
protected $regions = [];
|
||||
|
||||
/** @var Chunk[] */
|
||||
/** @var AnvilChunk[] */
|
||||
protected $chunks = [];
|
||||
|
||||
public static function getProviderName(){
|
||||
@ -49,10 +159,6 @@ class Anvil extends McRegion{
|
||||
return self::ORDER_YZX;
|
||||
}
|
||||
|
||||
public static function usesChunkSection(){
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function isValid($path){
|
||||
$isValid = (file_exists($path . "/level.dat") and is_dir($path . "/region/"));
|
||||
|
||||
@ -69,45 +175,9 @@ class Anvil extends McRegion{
|
||||
return $isValid;
|
||||
}
|
||||
|
||||
public function requestChunkTask($x, $z){
|
||||
$chunk = $this->getChunk($x, $z, false);
|
||||
if(!($chunk instanceof Chunk)){
|
||||
throw new ChunkException("Invalid Chunk sent");
|
||||
}
|
||||
|
||||
$tiles = "";
|
||||
|
||||
if(count($chunk->getTiles()) > 0){
|
||||
$nbt = new NBT(NBT::LITTLE_ENDIAN);
|
||||
$list = [];
|
||||
foreach($chunk->getTiles() as $tile){
|
||||
if($tile instanceof Spawnable){
|
||||
$list[] = $tile->getSpawnCompound();
|
||||
}
|
||||
}
|
||||
$nbt->setData($list);
|
||||
$tiles = $nbt->write(true);
|
||||
}
|
||||
|
||||
$extraData = new BinaryStream();
|
||||
$extraData->putLInt(count($chunk->getBlockExtraDataArray()));
|
||||
foreach($chunk->getBlockExtraDataArray() as $key => $value){
|
||||
$extraData->putLInt($key);
|
||||
$extraData->putLShort($value);
|
||||
}
|
||||
|
||||
$ordered = $chunk->getBlockIdArray() .
|
||||
$chunk->getBlockDataArray() .
|
||||
$chunk->getBlockSkyLightArray() .
|
||||
$chunk->getBlockLightArray() .
|
||||
pack("C*", ...$chunk->getHeightMapArray()) .
|
||||
pack("N*", ...$chunk->getBiomeColorArray()) .
|
||||
$extraData->getBuffer() .
|
||||
$tiles;
|
||||
|
||||
$this->getLevel()->chunkRequestCallback($x, $z, $ordered);
|
||||
|
||||
return null;
|
||||
public function getWorldHeight() : int{
|
||||
//TODO: add world height options
|
||||
return 256;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -117,55 +187,7 @@ class Anvil extends McRegion{
|
||||
* @return RegionLoader
|
||||
*/
|
||||
protected function getRegion($x, $z){
|
||||
return isset($this->regions[$index = Level::chunkHash($x, $z)]) ? $this->regions[$index] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param bool $create
|
||||
*
|
||||
* @return Chunk
|
||||
*/
|
||||
public function getChunk($chunkX, $chunkZ, $create = false){
|
||||
return parent::getChunk($chunkX, $chunkZ, $create);
|
||||
}
|
||||
|
||||
public function setChunk($chunkX, $chunkZ, FullChunk $chunk){
|
||||
if(!($chunk instanceof Chunk)){
|
||||
throw new ChunkException("Invalid Chunk class");
|
||||
}
|
||||
|
||||
$chunk->setProvider($this);
|
||||
|
||||
self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ);
|
||||
$this->loadRegion($regionX, $regionZ);
|
||||
|
||||
$chunk->setX($chunkX);
|
||||
$chunk->setZ($chunkZ);
|
||||
$this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $chunk;
|
||||
}
|
||||
|
||||
public function getEmptyChunk($chunkX, $chunkZ){
|
||||
return Chunk::getEmptyChunk($chunkX, $chunkZ, $this);
|
||||
}
|
||||
|
||||
public static function createChunkSection($Y){
|
||||
return new ChunkSection(new CompoundTag("", [
|
||||
"Y" => new ByteTag("Y", $Y),
|
||||
"Blocks" => new ByteArrayTag("Blocks", str_repeat("\x00", 4096)),
|
||||
"Data" => new ByteArrayTag("Data", str_repeat("\x00", 2048)),
|
||||
"SkyLight" => new ByteArrayTag("SkyLight", str_repeat("\xff", 2048)),
|
||||
"BlockLight" => new ByteArrayTag("BlockLight", str_repeat("\x00", 2048))
|
||||
]));
|
||||
}
|
||||
|
||||
public function isChunkGenerated($chunkX, $chunkZ){
|
||||
if(($region = $this->getRegion($chunkX >> 5, $chunkZ >> 5)) !== null){
|
||||
return $region->chunkExists($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32) and $this->getChunk($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32, true)->isGenerated();
|
||||
}
|
||||
|
||||
return false;
|
||||
return $this->regions[Level::chunkHash($x, $z)] ?? null;
|
||||
}
|
||||
|
||||
protected function loadRegion($x, $z){
|
||||
|
@ -1,368 +0,0 @@
|
||||
<?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\anvil;
|
||||
|
||||
use pocketmine\level\format\generic\BaseChunk;
|
||||
use pocketmine\level\format\generic\EmptyChunkSection;
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\tag\ByteArrayTag;
|
||||
use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\IntArrayTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\nbt\tag\ListTag;
|
||||
use pocketmine\nbt\tag\LongTag;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
|
||||
class Chunk extends BaseChunk{
|
||||
|
||||
/** @var CompoundTag */
|
||||
protected $nbt;
|
||||
|
||||
public function __construct($level, CompoundTag $nbt = null){
|
||||
if($nbt === null){
|
||||
$this->provider = $level;
|
||||
$this->nbt = new CompoundTag("Level", []);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->nbt = $nbt;
|
||||
|
||||
if(!isset($this->nbt->Entities) or !($this->nbt->Entities instanceof ListTag)){
|
||||
$this->nbt->Entities = new ListTag("Entities", []);
|
||||
$this->nbt->Entities->setTagType(NBT::TAG_Compound);
|
||||
}
|
||||
|
||||
if(!isset($this->nbt->TileEntities) or !($this->nbt->TileEntities instanceof ListTag)){
|
||||
$this->nbt->TileEntities = new ListTag("TileEntities", []);
|
||||
$this->nbt->TileEntities->setTagType(NBT::TAG_Compound);
|
||||
}
|
||||
|
||||
if(!isset($this->nbt->TileTicks) or !($this->nbt->TileTicks instanceof ListTag)){
|
||||
$this->nbt->TileTicks = new ListTag("TileTicks", []);
|
||||
$this->nbt->TileTicks->setTagType(NBT::TAG_Compound);
|
||||
}
|
||||
|
||||
if(!isset($this->nbt->Sections) or !($this->nbt->Sections instanceof ListTag)){
|
||||
$this->nbt->Sections = new ListTag("Sections", []);
|
||||
$this->nbt->Sections->setTagType(NBT::TAG_Compound);
|
||||
}
|
||||
|
||||
if(!isset($this->nbt->BiomeColors) or !($this->nbt->BiomeColors instanceof IntArrayTag)){
|
||||
$this->nbt->BiomeColors = new IntArrayTag("BiomeColors", array_fill(0, 256, 0));
|
||||
}
|
||||
|
||||
if(!isset($this->nbt->HeightMap) or !($this->nbt->HeightMap instanceof IntArrayTag)){
|
||||
$this->nbt->HeightMap = new IntArrayTag("HeightMap", array_fill(0, 256, 0));
|
||||
}
|
||||
|
||||
$sections = [];
|
||||
foreach($this->nbt->Sections as $section){
|
||||
if($section instanceof CompoundTag){
|
||||
$y = (int) $section["Y"];
|
||||
if($y < 8){
|
||||
$sections[$y] = new ChunkSection($section);
|
||||
}
|
||||
}
|
||||
}
|
||||
for($y = 0; $y < 8; ++$y){
|
||||
if(!isset($sections[$y])){
|
||||
$sections[$y] = new EmptyChunkSection($y);
|
||||
}
|
||||
}
|
||||
|
||||
$extraData = [];
|
||||
|
||||
if(!isset($this->nbt->ExtraData) or !($this->nbt->ExtraData instanceof ByteArrayTag)){
|
||||
$this->nbt->ExtraData = new ByteArrayTag("ExtraData", Binary::writeInt(0));
|
||||
}else{
|
||||
$stream = new BinaryStream($this->nbt->ExtraData->getValue());
|
||||
$count = $stream->getInt();
|
||||
for($i = 0; $i < $count; ++$i){
|
||||
$key = $stream->getInt();
|
||||
$extraData[$key] = $stream->getShort(false);
|
||||
}
|
||||
}
|
||||
|
||||
parent::__construct($level, (int) $this->nbt["xPos"], (int) $this->nbt["zPos"], $sections, $this->nbt->BiomeColors->getValue(), $this->nbt->HeightMap->getValue(), $this->nbt->Entities->getValue(), $this->nbt->TileEntities->getValue(), $extraData);
|
||||
|
||||
if(isset($this->nbt->Biomes)){
|
||||
$this->checkOldBiomes($this->nbt->Biomes->getValue());
|
||||
unset($this->nbt->Biomes);
|
||||
}
|
||||
|
||||
unset($this->nbt->Sections, $this->nbt->ExtraData);
|
||||
}
|
||||
|
||||
public function isLightPopulated(){
|
||||
return $this->nbt["LightPopulated"] > 0;
|
||||
}
|
||||
|
||||
public function setLightPopulated($value = 1){
|
||||
$this->nbt->LightPopulated = new ByteTag("LightPopulated", $value);
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isPopulated(){
|
||||
return $this->nbt["TerrainPopulated"] > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $value
|
||||
*/
|
||||
public function setPopulated($value = 1){
|
||||
$this->nbt->TerrainPopulated = new ByteTag("TerrainPopulated", $value);
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isGenerated(){
|
||||
return $this->nbt["TerrainPopulated"] > 0 or (isset($this->nbt->TerrainGenerated) and $this->nbt["TerrainGenerated"] > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $value
|
||||
*/
|
||||
public function setGenerated($value = 1){
|
||||
$this->nbt->TerrainGenerated = new ByteTag("TerrainGenerated", $value);
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CompoundTag
|
||||
*/
|
||||
public function getNBT(){
|
||||
return $this->nbt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
* @param LevelProvider $provider
|
||||
*
|
||||
* @return Chunk
|
||||
*/
|
||||
public static function fromBinary($data, LevelProvider $provider = null){
|
||||
$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)){
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Chunk($provider instanceof LevelProvider ? $provider : Anvil::class, $chunk->Level);
|
||||
}catch(\Throwable $e){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
* @param LevelProvider $provider
|
||||
*
|
||||
* @return Chunk
|
||||
*/
|
||||
public static function fromFastBinary($data, LevelProvider $provider = null){
|
||||
$nbt = new NBT(NBT::BIG_ENDIAN);
|
||||
|
||||
try{
|
||||
$nbt->read($data);
|
||||
$chunk = $nbt->getData();
|
||||
|
||||
if(!isset($chunk->Level) or !($chunk->Level instanceof CompoundTag)){
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Chunk($provider instanceof LevelProvider ? $provider : Anvil::class, $chunk->Level);
|
||||
}catch(\Throwable $e){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function toFastBinary(){
|
||||
$nbt = clone $this->getNBT();
|
||||
|
||||
$nbt->xPos = new IntTag("xPos", $this->x);
|
||||
$nbt->zPos = new IntTag("zPos", $this->z);
|
||||
|
||||
$nbt->Sections = new ListTag("Sections", []);
|
||||
$nbt->Sections->setTagType(NBT::TAG_Compound);
|
||||
foreach($this->getSections() as $section){
|
||||
if($section instanceof EmptyChunkSection){
|
||||
continue;
|
||||
}
|
||||
$nbt->Sections[$section->getY()] = new CompoundTag(null, [
|
||||
"Y" => new ByteTag("Y", $section->getY()),
|
||||
"Blocks" => new ByteArrayTag("Blocks", $section->getIdArray()),
|
||||
"Data" => new ByteArrayTag("Data", $section->getDataArray()),
|
||||
"BlockLight" => new ByteArrayTag("BlockLight", $section->getLightArray()),
|
||||
"SkyLight" => new ByteArrayTag("SkyLight", $section->getSkyLightArray())
|
||||
]);
|
||||
}
|
||||
|
||||
$nbt->BiomeColors = new IntArrayTag("BiomeColors", $this->getBiomeColorArray());
|
||||
|
||||
$nbt->HeightMap = new IntArrayTag("HeightMap", $this->getHeightMapArray());
|
||||
|
||||
$entities = [];
|
||||
|
||||
foreach($this->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($this->getTiles() as $tile){
|
||||
$tile->saveNBT();
|
||||
$tiles[] = $tile->namedtag;
|
||||
}
|
||||
|
||||
$nbt->TileEntities = new ListTag("TileEntities", $tiles);
|
||||
$nbt->TileEntities->setTagType(NBT::TAG_Compound);
|
||||
|
||||
$extraData = new BinaryStream();
|
||||
$extraData->putInt(count($this->getBlockExtraDataArray()));
|
||||
foreach($this->getBlockExtraDataArray() as $key => $value){
|
||||
$extraData->putInt($key);
|
||||
$extraData->putShort($value);
|
||||
}
|
||||
|
||||
$nbt->ExtraData = new ByteArrayTag("ExtraData", $extraData->getBuffer());
|
||||
|
||||
$writer = new NBT(NBT::BIG_ENDIAN);
|
||||
$nbt->setName("Level");
|
||||
$writer->setData(new CompoundTag("", ["Level" => $nbt]));
|
||||
|
||||
return $writer->write();
|
||||
}
|
||||
|
||||
public function toBinary(){
|
||||
$nbt = clone $this->getNBT();
|
||||
|
||||
$nbt->xPos = new IntTag("xPos", $this->x);
|
||||
$nbt->zPos = new IntTag("zPos", $this->z);
|
||||
|
||||
$nbt->Sections = new ListTag("Sections", []);
|
||||
$nbt->Sections->setTagType(NBT::TAG_Compound);
|
||||
foreach($this->getSections() as $section){
|
||||
if($section instanceof EmptyChunkSection){
|
||||
continue;
|
||||
}
|
||||
$nbt->Sections[$section->getY()] = new CompoundTag(null, [
|
||||
"Y" => new ByteTag("Y", $section->getY()),
|
||||
"Blocks" => new ByteArrayTag("Blocks", $section->getIdArray()),
|
||||
"Data" => new ByteArrayTag("Data", $section->getDataArray()),
|
||||
"BlockLight" => new ByteArrayTag("BlockLight", $section->getLightArray()),
|
||||
"SkyLight" => new ByteArrayTag("SkyLight", $section->getSkyLightArray())
|
||||
]);
|
||||
}
|
||||
|
||||
$nbt->BiomeColors = new IntArrayTag("BiomeColors", $this->getBiomeColorArray());
|
||||
|
||||
$nbt->HeightMap = new IntArrayTag("HeightMap", $this->getHeightMapArray());
|
||||
|
||||
$entities = [];
|
||||
|
||||
foreach($this->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($this->getTiles() as $tile){
|
||||
$tile->saveNBT();
|
||||
$tiles[] = $tile->namedtag;
|
||||
}
|
||||
|
||||
$nbt->TileEntities = new ListTag("TileEntities", $tiles);
|
||||
$nbt->TileEntities->setTagType(NBT::TAG_Compound);
|
||||
|
||||
$extraData = new BinaryStream();
|
||||
$extraData->putInt(count($this->getBlockExtraDataArray()));
|
||||
foreach($this->getBlockExtraDataArray() as $key => $value){
|
||||
$extraData->putInt($key);
|
||||
$extraData->putShort($value);
|
||||
}
|
||||
|
||||
$nbt->ExtraData = new ByteArrayTag("ExtraData", $extraData->getBuffer());
|
||||
|
||||
$writer = new NBT(NBT::BIG_ENDIAN);
|
||||
$nbt->setName("Level");
|
||||
$writer->setData(new CompoundTag("", ["Level" => $nbt]));
|
||||
|
||||
return $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param LevelProvider $provider
|
||||
*
|
||||
* @return Chunk
|
||||
*/
|
||||
public static function getEmptyChunk($chunkX, $chunkZ, LevelProvider $provider = null){
|
||||
try{
|
||||
$chunk = new Chunk($provider instanceof LevelProvider ? $provider : Anvil::class, null);
|
||||
$chunk->x = $chunkX;
|
||||
$chunk->z = $chunkZ;
|
||||
|
||||
for($y = 0; $y < 8; ++$y){
|
||||
$chunk->sections[$y] = new EmptyChunkSection($y);
|
||||
}
|
||||
|
||||
$chunk->heightMap = array_fill(0, 256, 0);
|
||||
$chunk->biomeColors = array_fill(0, 256, 0);
|
||||
|
||||
$chunk->nbt->V = new ByteTag("V", 1);
|
||||
$chunk->nbt->InhabitedTime = new LongTag("InhabitedTime", 0);
|
||||
$chunk->nbt->TerrainGenerated = new ByteTag("TerrainGenerated", 0);
|
||||
$chunk->nbt->TerrainPopulated = new ByteTag("TerrainPopulated", 0);
|
||||
$chunk->nbt->LightPopulated = new ByteTag("LightPopulated", 0);
|
||||
|
||||
return $chunk;
|
||||
}catch(\Throwable $e){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,226 +0,0 @@
|
||||
<?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\anvil;
|
||||
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
|
||||
class ChunkSection implements \pocketmine\level\format\ChunkSection{
|
||||
|
||||
private $y;
|
||||
private $blocks;
|
||||
private $data;
|
||||
private $blockLight;
|
||||
private $skyLight;
|
||||
|
||||
public function __construct(CompoundTag $nbt){
|
||||
$this->y = (int) $nbt["Y"];
|
||||
$this->blocks = (string) $nbt["Blocks"];
|
||||
$this->data = (string) $nbt["Data"];
|
||||
$this->blockLight = (string) $nbt["BlockLight"];
|
||||
$this->skyLight = (string) $nbt["SkyLight"];
|
||||
}
|
||||
|
||||
public function getY(){
|
||||
return $this->y;
|
||||
}
|
||||
|
||||
public function getBlockId($x, $y, $z){
|
||||
return ord($this->blocks{($y << 8) + ($z << 4) + $x});
|
||||
}
|
||||
|
||||
public function setBlockId($x, $y, $z, $id){
|
||||
$this->blocks{($y << 8) + ($z << 4) + $x} = chr($id);
|
||||
}
|
||||
|
||||
public function getBlockData($x, $y, $z){
|
||||
$m = ord($this->data{($y << 7) + ($z << 3) + ($x >> 1)});
|
||||
if(($x & 1) === 0){
|
||||
return $m & 0x0F;
|
||||
}else{
|
||||
return $m >> 4;
|
||||
}
|
||||
}
|
||||
|
||||
public function setBlockData($x, $y, $z, $data){
|
||||
$i = ($y << 7) + ($z << 3) + ($x >> 1);
|
||||
$old_m = ord($this->data{$i});
|
||||
if(($x & 1) === 0){
|
||||
$this->data{$i} = chr(($old_m & 0xf0) | ($data & 0x0f));
|
||||
}else{
|
||||
$this->data{$i} = chr((($data & 0x0f) << 4) | ($old_m & 0x0f));
|
||||
}
|
||||
}
|
||||
|
||||
public function getFullBlock($x, $y, $z){
|
||||
$i = ($y << 8) + ($z << 4) + $x;
|
||||
if(($x & 1) === 0){
|
||||
return (ord($this->blocks{$i}) << 4) | (ord($this->data{$i >> 1}) & 0x0F);
|
||||
}else{
|
||||
return (ord($this->blocks{$i}) << 4) | (ord($this->data{$i >> 1}) >> 4);
|
||||
}
|
||||
}
|
||||
|
||||
public function setBlock($x, $y, $z, $blockId = null, $meta = null){
|
||||
$i = ($y << 8) + ($z << 4) + $x;
|
||||
|
||||
$changed = false;
|
||||
|
||||
if($blockId !== null){
|
||||
$blockId = chr($blockId);
|
||||
if($this->blocks{$i} !== $blockId){
|
||||
$this->blocks{$i} = $blockId;
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if($meta !== null){
|
||||
$i >>= 1;
|
||||
$old_m = ord($this->data{$i});
|
||||
if(($x & 1) === 0){
|
||||
$this->data{$i} = chr(($old_m & 0xf0) | ($meta & 0x0f));
|
||||
if(($old_m & 0x0f) !== $meta){
|
||||
$changed = true;
|
||||
}
|
||||
}else{
|
||||
$this->data{$i} = chr((($meta & 0x0f) << 4) | ($old_m & 0x0f));
|
||||
if((($old_m & 0xf0) >> 4) !== $meta){
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $changed;
|
||||
}
|
||||
|
||||
public function getBlockSkyLight($x, $y, $z){
|
||||
$sl = ord($this->skyLight{($y << 7) + ($z << 3) + ($x >> 1)});
|
||||
if(($x & 1) === 0){
|
||||
return $sl & 0x0F;
|
||||
}else{
|
||||
return $sl >> 4;
|
||||
}
|
||||
}
|
||||
|
||||
public function setBlockSkyLight($x, $y, $z, $level){
|
||||
$i = ($y << 7) + ($z << 3) + ($x >> 1);
|
||||
$old_sl = ord($this->skyLight{$i});
|
||||
if(($x & 1) === 0){
|
||||
$this->skyLight{$i} = chr(($old_sl & 0xf0) | ($level & 0x0f));
|
||||
}else{
|
||||
$this->skyLight{$i} = chr((($level & 0x0f) << 4) | ($old_sl & 0x0f));
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockLight($x, $y, $z){
|
||||
$l = ord($this->blockLight{($y << 7) + ($z << 3) + ($x >> 1)});
|
||||
if(($x & 1) === 0){
|
||||
return $l & 0x0F;
|
||||
}else{
|
||||
return $l >> 4;
|
||||
}
|
||||
}
|
||||
|
||||
public function setBlockLight($x, $y, $z, $level){
|
||||
$i = ($y << 7) + ($z << 3) + ($x >> 1);
|
||||
$old_l = ord($this->blockLight{$i});
|
||||
if(($x & 1) === 0){
|
||||
$this->blockLight{$i} = chr(($old_l & 0xf0) | ($level & 0x0f));
|
||||
}else{
|
||||
$this->blockLight{$i} = chr((($level & 0x0f) << 4) | ($old_l & 0x0f));
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockIdColumn($x, $z){
|
||||
$i = ($z << 4) + $x;
|
||||
$column = "";
|
||||
for($y = 0; $y < 16; ++$y){
|
||||
$column .= $this->blocks{($y << 8) + $i};
|
||||
}
|
||||
|
||||
return $column;
|
||||
}
|
||||
|
||||
public function getBlockDataColumn($x, $z){
|
||||
$i = ($z << 3) + ($x >> 1);
|
||||
$column = "";
|
||||
if(($x & 1) === 0){
|
||||
for($y = 0; $y < 16; $y += 2){
|
||||
$column .= ($this->data{($y << 7) + $i} & "\x0f") | chr((ord($this->data{(($y + 1) << 7) + $i}) & 0x0f) << 4);
|
||||
}
|
||||
}else{
|
||||
for($y = 0; $y < 16; $y += 2){
|
||||
$column .= chr((ord($this->data{($y << 7) + $i}) & 0xf0) >> 4) | ($this->data{(($y + 1) << 7) + $i} & "\xf0");
|
||||
}
|
||||
}
|
||||
|
||||
return $column;
|
||||
}
|
||||
|
||||
public function getBlockSkyLightColumn($x, $z){
|
||||
$i = ($z << 3) + ($x >> 1);
|
||||
$column = "";
|
||||
if(($x & 1) === 0){
|
||||
for($y = 0; $y < 16; $y += 2){
|
||||
$column .= ($this->skyLight{($y << 7) + $i} & "\x0f") | chr((ord($this->skyLight{(($y + 1) << 7) + $i}) & 0x0f) << 4);
|
||||
}
|
||||
}else{
|
||||
for($y = 0; $y < 16; $y += 2){
|
||||
$column .= chr((ord($this->skyLight{($y << 7) + $i}) & 0xf0) >> 4) | ($this->skyLight{(($y + 1) << 7) + $i} & "\xf0");
|
||||
}
|
||||
}
|
||||
|
||||
return $column;
|
||||
}
|
||||
|
||||
public function getBlockLightColumn($x, $z){
|
||||
$i = ($z << 3) + ($x >> 1);
|
||||
$column = "";
|
||||
if(($x & 1) === 0){
|
||||
for($y = 0; $y < 16; $y += 2){
|
||||
$column .= ($this->blockLight{($y << 7) + $i} & "\x0f") | chr((ord($this->blockLight{(($y + 1) << 7) + $i}) & 0x0f) << 4);
|
||||
}
|
||||
}else{
|
||||
for($y = 0; $y < 16; $y += 2){
|
||||
$column .= chr((ord($this->blockLight{($y << 7) + $i}) & 0xf0) >> 4) | ($this->blockLight{(($y + 1) << 7) + $i} & "\xf0");
|
||||
}
|
||||
}
|
||||
|
||||
return $column;
|
||||
}
|
||||
|
||||
public function getIdArray(){
|
||||
return $this->blocks;
|
||||
}
|
||||
|
||||
public function getDataArray(){
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function getSkyLightArray(){
|
||||
return $this->skyLight;
|
||||
}
|
||||
|
||||
public function getLightArray(){
|
||||
return $this->blockLight;
|
||||
}
|
||||
|
||||
}
|
@ -22,6 +22,7 @@
|
||||
namespace pocketmine\level\format\anvil;
|
||||
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
use pocketmine\level\format\generic\GenericChunk;
|
||||
|
||||
class RegionLoader extends \pocketmine\level\format\mcregion\RegionLoader{
|
||||
|
||||
@ -45,6 +46,14 @@ class RegionLoader extends \pocketmine\level\format\mcregion\RegionLoader{
|
||||
}
|
||||
|
||||
protected function unserializeChunk($data){
|
||||
return Chunk::fromBinary($data, $this->levelProvider);
|
||||
return Anvil::nbtDeserialize($data, $this->levelProvider);
|
||||
}
|
||||
|
||||
public function writeChunk(GenericChunk $chunk){
|
||||
$this->lastUsed = time();
|
||||
$chunkData = Anvil::nbtSerialize($chunk);
|
||||
if($chunkData !== false){
|
||||
$this->saveChunk($chunk->getX() - ($this->getX() * 32), $chunk->getZ() - ($this->getZ() * 32), $chunkData);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,260 +0,0 @@
|
||||
<?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\generic;
|
||||
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\format\ChunkSection;
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\ChunkException;
|
||||
|
||||
abstract class BaseChunk extends BaseFullChunk implements Chunk{
|
||||
|
||||
/** @var ChunkSection[] */
|
||||
protected $sections = [];
|
||||
|
||||
/**
|
||||
* @param LevelProvider $provider
|
||||
* @param int $x
|
||||
* @param int $z
|
||||
* @param ChunkSection[] $sections
|
||||
* @param int[] $biomeColors
|
||||
* @param int[] $heightMap
|
||||
* @param CompoundTag[] $entities
|
||||
* @param CompoundTag[] $tiles
|
||||
*
|
||||
* @throws ChunkException
|
||||
*/
|
||||
protected function __construct($provider, $x, $z, array $sections, array $biomeColors = [], array $heightMap = [], array $entities = [], array $tiles = []){
|
||||
$this->provider = $provider;
|
||||
$this->x = (int) $x;
|
||||
$this->z = (int) $z;
|
||||
foreach($sections as $Y => $section){
|
||||
if($section instanceof ChunkSection){
|
||||
$this->sections[$Y] = $section;
|
||||
}else{
|
||||
throw new ChunkException("Received invalid ChunkSection instance");
|
||||
}
|
||||
|
||||
if($Y >= self::SECTION_COUNT){
|
||||
throw new ChunkException("Invalid amount of chunks");
|
||||
}
|
||||
}
|
||||
|
||||
if(count($biomeColors) === 256){
|
||||
$this->biomeColors = $biomeColors;
|
||||
}else{
|
||||
$this->biomeColors = array_fill(0, 256, Binary::readInt("\xff\x00\x00\x00"));
|
||||
}
|
||||
|
||||
if(count($heightMap) === 256){
|
||||
$this->heightMap = $heightMap;
|
||||
}else{
|
||||
$this->heightMap = array_fill(0, 256, 127);
|
||||
}
|
||||
|
||||
$this->NBTtiles = $tiles;
|
||||
$this->NBTentities = $entities;
|
||||
}
|
||||
|
||||
public function getFullBlock($x, $y, $z){
|
||||
return $this->sections[$y >> 4]->getFullBlock($x, $y & 0x0f, $z);
|
||||
}
|
||||
|
||||
public function setBlock($x, $y, $z, $blockId = null, $meta = null){
|
||||
try{
|
||||
$this->hasChanged = true;
|
||||
return $this->sections[$y >> 4]->setBlock($x, $y & 0x0f, $z, $blockId & 0xff, $meta & 0x0f);
|
||||
}catch(ChunkException $e){
|
||||
$level = $this->getProvider();
|
||||
$this->setInternalSection($Y = $y >> 4, $level::createChunkSection($Y));
|
||||
return $this->sections[$y >> 4]->setBlock($x, $y & 0x0f, $z, $blockId & 0xff, $meta & 0x0f);
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockId($x, $y, $z){
|
||||
return $this->sections[$y >> 4]->getBlockId($x, $y & 0x0f, $z);
|
||||
}
|
||||
|
||||
public function setBlockId($x, $y, $z, $id){
|
||||
try{
|
||||
$this->sections[$y >> 4]->setBlockId($x, $y & 0x0f, $z, $id);
|
||||
$this->hasChanged = true;
|
||||
}catch(ChunkException $e){
|
||||
$level = $this->getProvider();
|
||||
$this->setInternalSection($Y = $y >> 4, $level::createChunkSection($Y));
|
||||
$this->setBlockId($x, $y, $z, $id);
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockData($x, $y, $z){
|
||||
return $this->sections[$y >> 4]->getBlockData($x, $y & 0x0f, $z);
|
||||
}
|
||||
|
||||
public function setBlockData($x, $y, $z, $data){
|
||||
try{
|
||||
$this->sections[$y >> 4]->setBlockData($x, $y & 0x0f, $z, $data);
|
||||
$this->hasChanged = true;
|
||||
}catch(ChunkException $e){
|
||||
$level = $this->getProvider();
|
||||
$this->setInternalSection($Y = $y >> 4, $level::createChunkSection($Y));
|
||||
$this->setBlockData($x, $y, $z, $data);
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockSkyLight($x, $y, $z){
|
||||
return $this->sections[$y >> 4]->getBlockSkyLight($x, $y & 0x0f, $z);
|
||||
}
|
||||
|
||||
public function setBlockSkyLight($x, $y, $z, $data){
|
||||
try{
|
||||
$this->sections[$y >> 4]->setBlockSkyLight($x, $y & 0x0f, $z, $data);
|
||||
$this->hasChanged = true;
|
||||
}catch(ChunkException $e){
|
||||
$level = $this->getProvider();
|
||||
$this->setInternalSection($Y = $y >> 4, $level::createChunkSection($Y));
|
||||
$this->setBlockSkyLight($x, $y, $z, $data);
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockLight($x, $y, $z){
|
||||
return $this->sections[$y >> 4]->getBlockLight($x, $y & 0x0f, $z);
|
||||
}
|
||||
|
||||
public function setBlockLight($x, $y, $z, $data){
|
||||
try{
|
||||
$this->sections[$y >> 4]->setBlockLight($x, $y & 0x0f, $z, $data);
|
||||
$this->hasChanged = true;
|
||||
}catch(ChunkException $e){
|
||||
$level = $this->getProvider();
|
||||
$this->setInternalSection($Y = $y >> 4, $level::createChunkSection($Y));
|
||||
$this->setBlockLight($x, $y, $z, $data);
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockIdColumn($x, $z){
|
||||
$column = "";
|
||||
for($y = 0; $y < Chunk::SECTION_COUNT; ++$y){
|
||||
$column .= $this->sections[$y]->getBlockIdColumn($x, $z);
|
||||
}
|
||||
|
||||
return $column;
|
||||
}
|
||||
|
||||
public function getBlockDataColumn($x, $z){
|
||||
$column = "";
|
||||
for($y = 0; $y < Chunk::SECTION_COUNT; ++$y){
|
||||
$column .= $this->sections[$y]->getBlockDataColumn($x, $z);
|
||||
}
|
||||
|
||||
return $column;
|
||||
}
|
||||
|
||||
public function getBlockSkyLightColumn($x, $z){
|
||||
$column = "";
|
||||
for($y = 0; $y < Chunk::SECTION_COUNT; ++$y){
|
||||
$column .= $this->sections[$y]->getBlockSkyLightColumn($x, $z);
|
||||
}
|
||||
|
||||
return $column;
|
||||
}
|
||||
|
||||
public function getBlockLightColumn($x, $z){
|
||||
$column = "";
|
||||
for($y = 0; $y < Chunk::SECTION_COUNT; ++$y){
|
||||
$column .= $this->sections[$y]->getBlockLightColumn($x, $z);
|
||||
}
|
||||
|
||||
return $column;
|
||||
}
|
||||
|
||||
public function isSectionEmpty($fY){
|
||||
return $this->sections[(int) $fY] instanceof EmptyChunkSection;
|
||||
}
|
||||
|
||||
public function getSection($fY){
|
||||
return $this->sections[(int) $fY];
|
||||
}
|
||||
|
||||
public function setSection($fY, ChunkSection $section){
|
||||
if(substr_count($section->getIdArray(), "\x00") === 4096 and substr_count($section->getDataArray(), "\x00") === 2048){
|
||||
$this->sections[(int) $fY] = new EmptyChunkSection($fY);
|
||||
}else{
|
||||
$this->sections[(int) $fY] = $section;
|
||||
}
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
|
||||
private function setInternalSection($fY, ChunkSection $section){
|
||||
$this->sections[(int) $fY] = $section;
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
|
||||
public function load($generate = true){
|
||||
return $this->getProvider() === null ? false : $this->getProvider()->getChunk($this->getX(), $this->getZ(), true) instanceof Chunk;
|
||||
}
|
||||
|
||||
public function getBlockIdArray(){
|
||||
$blocks = "";
|
||||
for($y = 0; $y < Chunk::SECTION_COUNT; ++$y){
|
||||
$blocks .= $this->sections[$y]->getIdArray();
|
||||
}
|
||||
|
||||
return $blocks;
|
||||
}
|
||||
|
||||
public function getBlockDataArray(){
|
||||
$data = "";
|
||||
for($y = 0; $y < Chunk::SECTION_COUNT; ++$y){
|
||||
$data .= $this->sections[$y]->getDataArray();
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getBlockSkyLightArray(){
|
||||
$skyLight = "";
|
||||
for($y = 0; $y < Chunk::SECTION_COUNT; ++$y){
|
||||
$skyLight .= $this->sections[$y]->getSkyLightArray();
|
||||
}
|
||||
|
||||
return $skyLight;
|
||||
}
|
||||
|
||||
public function getBlockLightArray(){
|
||||
$blockLight = "";
|
||||
for($y = 0; $y < Chunk::SECTION_COUNT; ++$y){
|
||||
$blockLight .= $this->sections[$y]->getLightArray();
|
||||
}
|
||||
|
||||
return $blockLight;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ChunkSection[]
|
||||
*/
|
||||
public function getSections(){
|
||||
return $this->sections;
|
||||
}
|
||||
|
||||
}
|
@ -1,455 +0,0 @@
|
||||
<?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\generic;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
use pocketmine\level\generator\biome\Biome;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\tile\Tile;
|
||||
|
||||
|
||||
abstract class BaseFullChunk implements FullChunk{
|
||||
|
||||
/** @var Entity[] */
|
||||
protected $entities = [];
|
||||
|
||||
/** @var Tile[] */
|
||||
protected $tiles = [];
|
||||
|
||||
/** @var Tile[] */
|
||||
protected $tileList = [];
|
||||
|
||||
/** @var int[256] */
|
||||
protected $biomeColors;
|
||||
|
||||
protected $blocks;
|
||||
|
||||
protected $data;
|
||||
|
||||
protected $skyLight;
|
||||
|
||||
protected $blockLight;
|
||||
|
||||
protected $heightMap;
|
||||
|
||||
protected $NBTtiles;
|
||||
|
||||
protected $NBTentities;
|
||||
|
||||
protected $extraData = [];
|
||||
|
||||
/** @var LevelProvider */
|
||||
protected $provider;
|
||||
|
||||
protected $x;
|
||||
protected $z;
|
||||
|
||||
protected $hasChanged = false;
|
||||
|
||||
private $isInit = false;
|
||||
|
||||
/**
|
||||
* @param LevelProvider $provider
|
||||
* @param int $x
|
||||
* @param int $z
|
||||
* @param string $blocks
|
||||
* @param string $data
|
||||
* @param string $skyLight
|
||||
* @param string $blockLight
|
||||
* @param int[] $biomeColors
|
||||
* @param int[] $heightMap
|
||||
* @param CompoundTag[] $entities
|
||||
* @param CompoundTag[] $tiles
|
||||
*/
|
||||
protected function __construct($provider, $x, $z, $blocks, $data, $skyLight, $blockLight, array $biomeColors = [], array $heightMap = [], array $entities = [], array $tiles = [], array $extraData = []){
|
||||
$this->provider = $provider;
|
||||
$this->x = (int) $x;
|
||||
$this->z = (int) $z;
|
||||
|
||||
$this->blocks = $blocks;
|
||||
$this->data = $data;
|
||||
$this->skyLight = $skyLight;
|
||||
$this->blockLight = $blockLight;
|
||||
|
||||
if(count($biomeColors) === 256){
|
||||
$this->biomeColors = $biomeColors;
|
||||
}else{
|
||||
$this->biomeColors = array_fill(0, 256, 0);
|
||||
}
|
||||
|
||||
if(count($heightMap) === 256){
|
||||
$this->heightMap = $heightMap;
|
||||
}else{
|
||||
$this->heightMap = array_fill(0, 256, 127);
|
||||
}
|
||||
|
||||
$this->extraData = $extraData;
|
||||
|
||||
$this->NBTtiles = $tiles;
|
||||
$this->NBTentities = $entities;
|
||||
}
|
||||
|
||||
protected function checkOldBiomes($data){
|
||||
if(strlen($data) !== 256){
|
||||
return;
|
||||
}
|
||||
|
||||
for($x = 0; $x < 16; ++$x){
|
||||
for($z = 0; $z < 16; ++$z){
|
||||
$biome = Biome::getBiome(ord($data{($z << 4) + $x}));
|
||||
$this->setBiomeId($x, $z, $biome->getId());
|
||||
$c = $biome->getColor();
|
||||
$this->setBiomeColor($x, $z, $c >> 16, ($c >> 8) & 0xff, $c & 0xff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function initChunk(){
|
||||
if($this->getProvider() instanceof LevelProvider and !$this->isInit){
|
||||
$changed = false;
|
||||
if($this->NBTentities !== null){
|
||||
$this->getProvider()->getLevel()->timings->syncChunkLoadEntitiesTimer->startTiming();
|
||||
foreach($this->NBTentities as $nbt){
|
||||
if($nbt instanceof CompoundTag){
|
||||
if(!isset($nbt->id)){
|
||||
$this->setChanged();
|
||||
continue;
|
||||
}
|
||||
|
||||
if(($nbt["Pos"][0] >> 4) !== $this->x or ($nbt["Pos"][2] >> 4) !== $this->z){
|
||||
$changed = true;
|
||||
continue; //Fixes entities allocated in wrong chunks.
|
||||
}
|
||||
|
||||
if(($entity = Entity::createEntity($nbt["id"], $this, $nbt)) instanceof Entity){
|
||||
$entity->spawnToAll();
|
||||
}else{
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->getProvider()->getLevel()->timings->syncChunkLoadEntitiesTimer->stopTiming();
|
||||
|
||||
$this->getProvider()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->startTiming();
|
||||
foreach($this->NBTtiles as $nbt){
|
||||
if($nbt instanceof CompoundTag){
|
||||
if(!isset($nbt->id)){
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(($nbt["x"] >> 4) !== $this->x or ($nbt["z"] >> 4) !== $this->z){
|
||||
$changed = true;
|
||||
continue; //Fixes tiles allocated in wrong chunks.
|
||||
}
|
||||
|
||||
if(Tile::createTile($nbt["id"], $this, $nbt) === null){
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->getProvider()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->stopTiming();
|
||||
|
||||
$this->NBTentities = null;
|
||||
$this->NBTtiles = null;
|
||||
}
|
||||
|
||||
$this->setChanged($changed);
|
||||
|
||||
$this->isInit = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function getX(){
|
||||
return $this->x;
|
||||
}
|
||||
|
||||
public function getZ(){
|
||||
return $this->z;
|
||||
}
|
||||
|
||||
public function setX($x){
|
||||
$this->x = $x;
|
||||
}
|
||||
|
||||
public function setZ($z){
|
||||
$this->z = $z;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LevelProvider
|
||||
*/
|
||||
public function getProvider(){
|
||||
return $this->provider;
|
||||
}
|
||||
|
||||
public function setProvider(LevelProvider $provider){
|
||||
$this->provider = $provider;
|
||||
}
|
||||
|
||||
public function getBiomeId($x, $z){
|
||||
return ($this->biomeColors[($z << 4) + $x] & 0xFF000000) >> 24;
|
||||
}
|
||||
|
||||
public function setBiomeId($x, $z, $biomeId){
|
||||
$this->hasChanged = true;
|
||||
$this->biomeColors[($z << 4) + $x] = ($this->biomeColors[($z << 4) + $x] & 0xFFFFFF) | ($biomeId << 24);
|
||||
}
|
||||
|
||||
public function getBiomeColor($x, $z){
|
||||
$color = $this->biomeColors[($z << 4) + $x] & 0xFFFFFF;
|
||||
|
||||
return [$color >> 16, ($color >> 8) & 0xFF, $color & 0xFF];
|
||||
}
|
||||
|
||||
public function setBiomeColor($x, $z, $R, $G, $B){
|
||||
$this->hasChanged = true;
|
||||
$this->biomeColors[($z << 4) + $x] = ($this->biomeColors[($z << 4) + $x] & 0xFF000000) | (($R & 0xFF) << 16) | (($G & 0xFF) << 8) | ($B & 0xFF);
|
||||
}
|
||||
|
||||
public function getHeightMap($x, $z){
|
||||
return $this->heightMap[($z << 4) + $x];
|
||||
}
|
||||
|
||||
public function setHeightMap($x, $z, $value){
|
||||
$this->heightMap[($z << 4) + $x] = $value;
|
||||
}
|
||||
|
||||
public function recalculateHeightMap(){
|
||||
for($z = 0; $z < 16; ++$z){
|
||||
for($x = 0; $x < 16; ++$x){
|
||||
$this->setHeightMap($x, $z, $this->getHighestBlockAt($x, $z, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockExtraData($x, $y, $z){
|
||||
if(isset($this->extraData[$index = Level::chunkBlockHash($x, $y, $z)])){
|
||||
return $this->extraData[$index];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function setBlockExtraData($x, $y, $z, $data){
|
||||
if($data === 0){
|
||||
unset($this->extraData[Level::chunkBlockHash($x, $y, $z)]);
|
||||
}else{
|
||||
$this->extraData[Level::chunkBlockHash($x, $y, $z)] = $data;
|
||||
}
|
||||
|
||||
$this->setChanged(true);
|
||||
}
|
||||
|
||||
public function populateSkyLight(){
|
||||
for($z = 0; $z < 16; ++$z){
|
||||
for($x = 0; $x < 16; ++$x){
|
||||
$top = $this->getHeightMap($x, $z);
|
||||
for($y = 127; $y > $top; --$y){
|
||||
$this->setBlockSkyLight($x, $y, $z, 15);
|
||||
}
|
||||
|
||||
for($y = $top; $y >= 0; --$y){
|
||||
if(Block::$solid[$this->getBlockId($x, $y, $z)]){
|
||||
break;
|
||||
}
|
||||
|
||||
$this->setBlockSkyLight($x, $y, $z, 15);
|
||||
}
|
||||
|
||||
$this->setHeightMap($x, $z, $this->getHighestBlockAt($x, $z, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getHighestBlockAt($x, $z, $cache = true){
|
||||
if($cache){
|
||||
$h = $this->getHeightMap($x, $z);
|
||||
|
||||
if($h !== 0 and $h !== 127){
|
||||
return $h;
|
||||
}
|
||||
}
|
||||
|
||||
$column = $this->getBlockIdColumn($x, $z);
|
||||
for($y = 127; $y >= 0; --$y){
|
||||
if($column{$y} !== "\x00"){
|
||||
$this->setHeightMap($x, $z, $y);
|
||||
return $y;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function addEntity(Entity $entity){
|
||||
$this->entities[$entity->getId()] = $entity;
|
||||
if(!($entity instanceof Player) and $this->isInit){
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function removeEntity(Entity $entity){
|
||||
unset($this->entities[$entity->getId()]);
|
||||
if(!($entity instanceof Player) and $this->isInit){
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function addTile(Tile $tile){
|
||||
$this->tiles[$tile->getId()] = $tile;
|
||||
if(isset($this->tileList[$index = (($tile->z & 0x0f) << 12) | (($tile->x & 0x0f) << 8) | ($tile->y & 0xff)]) and $this->tileList[$index] !== $tile){
|
||||
$this->tileList[$index]->close();
|
||||
}
|
||||
$this->tileList[$index] = $tile;
|
||||
if($this->isInit){
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function removeTile(Tile $tile){
|
||||
unset($this->tiles[$tile->getId()]);
|
||||
unset($this->tileList[(($tile->z & 0x0f) << 12) | (($tile->x & 0x0f) << 8) | ($tile->y & 0xff)]);
|
||||
if($this->isInit){
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function getEntities(){
|
||||
return $this->entities;
|
||||
}
|
||||
|
||||
public function getTiles(){
|
||||
return $this->tiles;
|
||||
}
|
||||
|
||||
public function getBlockExtraDataArray(){
|
||||
return $this->extraData;
|
||||
}
|
||||
|
||||
public function getTile($x, $y, $z){
|
||||
$index = ($z << 12) | ($x << 8) | $y;
|
||||
return isset($this->tileList[$index]) ? $this->tileList[$index] : null;
|
||||
}
|
||||
|
||||
public function isLoaded(){
|
||||
return $this->getProvider() === null ? false : $this->getProvider()->isChunkLoaded($this->getX(), $this->getZ());
|
||||
}
|
||||
|
||||
public function load($generate = true){
|
||||
return $this->getProvider() === null ? false : $this->getProvider()->getChunk($this->getX(), $this->getZ(), true) instanceof FullChunk;
|
||||
}
|
||||
|
||||
public function unload($save = true, $safe = true){
|
||||
$level = $this->getProvider();
|
||||
if($level === null){
|
||||
return true;
|
||||
}
|
||||
if($save === true and $this->hasChanged){
|
||||
$level->saveChunk($this->getX(), $this->getZ());
|
||||
}
|
||||
if($safe === true){
|
||||
foreach($this->getEntities() as $entity){
|
||||
if($entity instanceof Player){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach($this->getEntities() as $entity){
|
||||
if($entity instanceof Player){
|
||||
continue;
|
||||
}
|
||||
$entity->close();
|
||||
}
|
||||
foreach($this->getTiles() as $tile){
|
||||
$tile->close();
|
||||
}
|
||||
$this->provider = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getBlockIdArray(){
|
||||
return $this->blocks;
|
||||
}
|
||||
|
||||
public function getBlockDataArray(){
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function getBlockSkyLightArray(){
|
||||
return $this->skyLight;
|
||||
}
|
||||
|
||||
public function getBlockLightArray(){
|
||||
return $this->blockLight;
|
||||
}
|
||||
|
||||
public function getBiomeIdArray(){
|
||||
$ids = "";
|
||||
foreach($this->biomeColors as $d){
|
||||
$ids .= chr(($d & 0xFF000000) >> 24);
|
||||
}
|
||||
return $ids;
|
||||
}
|
||||
|
||||
public function getBiomeColorArray(){
|
||||
return $this->biomeColors;
|
||||
}
|
||||
|
||||
public function getHeightMapArray(){
|
||||
return $this->heightMap;
|
||||
}
|
||||
|
||||
public function hasChanged(){
|
||||
return $this->hasChanged;
|
||||
}
|
||||
|
||||
public function setChanged($changed = true){
|
||||
$this->hasChanged = (bool) $changed;
|
||||
}
|
||||
|
||||
public static function fromFastBinary($data, LevelProvider $provider = null){
|
||||
return static::fromBinary($data, $provider);
|
||||
}
|
||||
|
||||
public function toFastBinary(){
|
||||
return $this->toBinary();
|
||||
}
|
||||
|
||||
public function isLightPopulated(){
|
||||
return true;
|
||||
}
|
||||
|
||||
public function setLightPopulated($value = 1){
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -126,5 +126,44 @@ abstract class BaseLevelProvider implements LevelProvider{
|
||||
file_put_contents($this->getPath() . "level.dat", $buffer);
|
||||
}
|
||||
|
||||
public function requestChunkTask($x, $z){
|
||||
$chunk = $this->getChunk($x, $z, false);
|
||||
if(!($chunk instanceof GenericChunk)){
|
||||
throw new ChunkException("Invalid Chunk sent");
|
||||
}
|
||||
|
||||
/*
|
||||
$tiles = "";
|
||||
|
||||
if(count($chunk->getTiles()) > 0){
|
||||
$nbt = new NBT(NBT::LITTLE_ENDIAN);
|
||||
$list = [];
|
||||
foreach($chunk->getTiles() as $tile){
|
||||
if($tile instanceof Spawnable){
|
||||
$list[] = $tile->getSpawnCompound();
|
||||
}
|
||||
}
|
||||
$nbt->setData($list);
|
||||
$tiles = $nbt->write(true);
|
||||
}
|
||||
|
||||
$extraData = new BinaryStream();
|
||||
$extraData->putLInt(count($chunk->getBlockExtraDataArray()));
|
||||
foreach($chunk->getBlockExtraDataArray() as $key => $value){
|
||||
$extraData->putLInt($key);
|
||||
$extraData->putLShort($value);
|
||||
}
|
||||
|
||||
$ordered = $chunk->getBlockIdArray() .
|
||||
$chunk->getBlockDataArray() .
|
||||
$chunk->getBlockSkyLightArray() .
|
||||
$chunk->getBlockLightArray() .
|
||||
pack("C*", ...$chunk->getHeightMapArray()) .
|
||||
pack("N*", ...$chunk->getBiomeColorArray()) .
|
||||
$extraData->getBuffer() .
|
||||
$tiles;
|
||||
*/
|
||||
|
||||
$this->getLevel()->chunkRequestCallback($x, $z, $chunk->networkSerialize());
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\format\anvil;
|
||||
namespace pocketmine\level\format;
|
||||
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\nbt\NBT;
|
||||
@ -40,10 +40,11 @@ class ChunkRequestTask extends AsyncTask{
|
||||
public function __construct(Level $level, Chunk $chunk){
|
||||
$this->levelId = $level->getId();
|
||||
|
||||
$this->chunk = $chunk->toFastBinary();
|
||||
$this->chunk = GenericChunk::fastSerialize($chunk);
|
||||
$this->chunkX = $chunk->getX();
|
||||
$this->chunkZ = $chunk->getZ();
|
||||
|
||||
//TODO: serialize tiles with chunks
|
||||
$tiles = "";
|
||||
$nbt = new NBT(NBT::LITTLE_ENDIAN);
|
||||
foreach($chunk->getTiles() as $tile){
|
||||
@ -57,63 +58,13 @@ class ChunkRequestTask extends AsyncTask{
|
||||
}
|
||||
|
||||
public function onRun(){
|
||||
$chunk = GenericChunk::fastDeserialize($this->chunk);
|
||||
|
||||
$chunk = Chunk::fromFastBinary($this->chunk);
|
||||
$ids = $chunk->getBlockIdArray();
|
||||
$meta = $chunk->getBlockDataArray();
|
||||
$blockLight = $chunk->getBlockLightArray();
|
||||
$skyLight = $chunk->getBlockSkyLightArray();
|
||||
|
||||
|
||||
$orderedIds = "";
|
||||
$orderedData = "";
|
||||
$orderedSkyLight = "";
|
||||
$orderedLight = "";
|
||||
|
||||
|
||||
for($x = 0; $x < 16; ++$x){
|
||||
for($z = 0; $z < 16; ++$z){
|
||||
$orderedIds .= $this->getColumn($ids, $x, $z);
|
||||
$orderedData .= $this->getHalfColumn($meta, $x, $z);
|
||||
$orderedSkyLight .= $this->getHalfColumn($skyLight, $x, $z);
|
||||
$orderedLight .= $this->getHalfColumn($blockLight, $x, $z);
|
||||
}
|
||||
}
|
||||
|
||||
$heightmap = pack("C*", ...$chunk->getHeightMapArray());
|
||||
$biomeColors = pack("N*", ...$chunk->getBiomeColorArray());
|
||||
|
||||
$ordered = $orderedIds . $orderedData . $orderedSkyLight . $orderedLight . $heightmap . $biomeColors . $this->tiles;
|
||||
$ordered = $chunk->networkSerialize();
|
||||
|
||||
$this->setResult($ordered, false);
|
||||
}
|
||||
|
||||
public function getColumn($data, $x, $z){
|
||||
$column = "";
|
||||
$i = ($z << 4) + $x;
|
||||
for($y = 0; $y < 128; ++$y){
|
||||
$column .= $data{($y << 8) + $i};
|
||||
}
|
||||
|
||||
return $column;
|
||||
}
|
||||
|
||||
public function getHalfColumn($data, $x, $z){
|
||||
$column = "";
|
||||
$i = ($z << 3) + ($x >> 1);
|
||||
if(($x & 1) === 0){
|
||||
for($y = 0; $y < 128; $y += 2){
|
||||
$column .= ($data{($y << 7) + $i} & "\x0f") | chr((ord($data{(($y + 1) << 7) + $i}) & 0x0f) << 4);
|
||||
}
|
||||
}else{
|
||||
for($y = 0; $y < 128; $y += 2){
|
||||
$column .= chr((ord($data{($y << 7) + $i}) & 0xf0) >> 4) | ($data{(($y + 1) << 7) + $i} & "\xf0");
|
||||
}
|
||||
}
|
||||
|
||||
return $column;
|
||||
}
|
||||
|
||||
public function onCompletion(Server $server){
|
||||
$level = $server->getLevel($this->levelId);
|
||||
if($level instanceof Level and $this->hasResult()){
|
@ -1,118 +0,0 @@
|
||||
<?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\generic;
|
||||
|
||||
use pocketmine\level\format\ChunkSection;
|
||||
use pocketmine\utils\ChunkException;
|
||||
|
||||
/**
|
||||
* Stub used to detect empty chunks
|
||||
*/
|
||||
class EmptyChunkSection implements ChunkSection{
|
||||
|
||||
private $y;
|
||||
|
||||
public function __construct($y){
|
||||
$this->y = $y;
|
||||
}
|
||||
|
||||
final public function getY(){
|
||||
return $this->y;
|
||||
}
|
||||
|
||||
final public function getBlockId($x, $y, $z){
|
||||
return 0;
|
||||
}
|
||||
|
||||
final public function getBlockIdColumn($x, $z){
|
||||
return "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
}
|
||||
|
||||
final public function getBlockDataColumn($x, $z){
|
||||
return "\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
}
|
||||
|
||||
final public function getBlockSkyLightColumn($x, $z){
|
||||
return "\xff\xff\xff\xff\xff\xff\xff\xff";
|
||||
}
|
||||
|
||||
final public function getBlockLightColumn($x, $z){
|
||||
return "\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
}
|
||||
|
||||
final public function getFullBlock($x, $y, $z){
|
||||
return 0;
|
||||
}
|
||||
|
||||
final public function getBlock($x, $y, $z, &$id = null, &$meta = null){
|
||||
$id = 0;
|
||||
$meta = 0;
|
||||
}
|
||||
|
||||
final public function setBlock($x, $y, $z, $id = null, $meta = null){
|
||||
throw new ChunkException("Tried to modify an empty Chunk");
|
||||
}
|
||||
|
||||
public function getIdArray(){
|
||||
return str_repeat("\x00", 4096);
|
||||
}
|
||||
|
||||
public function getDataArray(){
|
||||
return str_repeat("\x00", 2048);
|
||||
}
|
||||
|
||||
public function getSkyLightArray(){
|
||||
return str_repeat("\xff", 2048);
|
||||
}
|
||||
|
||||
public function getLightArray(){
|
||||
return str_repeat("\x00", 2048);
|
||||
}
|
||||
|
||||
final public function setBlockId($x, $y, $z, $id){
|
||||
throw new ChunkException("Tried to modify an empty Chunk");
|
||||
}
|
||||
|
||||
final public function getBlockData($x, $y, $z){
|
||||
return 0;
|
||||
}
|
||||
|
||||
final public function setBlockData($x, $y, $z, $data){
|
||||
throw new ChunkException("Tried to modify an empty Chunk");
|
||||
}
|
||||
|
||||
final public function getBlockLight($x, $y, $z){
|
||||
return 0;
|
||||
}
|
||||
|
||||
final public function setBlockLight($x, $y, $z, $level){
|
||||
throw new ChunkException("Tried to modify an empty Chunk");
|
||||
}
|
||||
|
||||
final public function getBlockSkyLight($x, $y, $z){
|
||||
return 15;
|
||||
}
|
||||
|
||||
final public function setBlockSkyLight($x, $y, $z, $level){
|
||||
throw new ChunkException("Tried to modify an empty Chunk");
|
||||
}
|
||||
}
|
108
src/pocketmine/level/format/generic/EmptySubChunk.php
Normal file
108
src/pocketmine/level/format/generic/EmptySubChunk.php
Normal file
@ -0,0 +1,108 @@
|
||||
<?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\generic;
|
||||
|
||||
use pocketmine\utils\ChunkException;
|
||||
|
||||
class EmptySubChunk extends SubChunk{
|
||||
protected $y;
|
||||
|
||||
public function __construct(int $y){
|
||||
$this->y = $y;
|
||||
}
|
||||
|
||||
public function isEmpty() : bool{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getBlockId(int $x, int $y, int $z) : int{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function setBlockId(int $x, int $y, int $z, int $id){
|
||||
throw new ChunkException("Tried to modify nonexistent subchunk");
|
||||
}
|
||||
|
||||
public function getBlockData(int $x, int $y, int $z) : int{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function setBlockData(int $x, int $y, int $z, int $data){
|
||||
throw new ChunkException("Tried to modify nonexistent subchunk");
|
||||
}
|
||||
|
||||
public function getFullBlock(int $x, int $y, int $z) : int{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function setBlock(int $x, int $y, int $z, $id = null, $data = null) : bool{
|
||||
throw new ChunkException("Tried to modify nonexistent subchunk");
|
||||
}
|
||||
|
||||
public function getBlockSkyLight(int $x, int $y, int $z) : int{
|
||||
return 15;
|
||||
}
|
||||
|
||||
public function setBlockSkyLight(int $x, int $y, int $z, int $level){
|
||||
throw new ChunkException("Tried to modify nonexistent subchunk");
|
||||
}
|
||||
|
||||
public function getBlockLight(int $x, int $y, int $z) : int{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function setBlockLight(int $x, int $y, int $z, int $level){
|
||||
throw new ChunkException("Tried to modify nonexistent subchunk");
|
||||
}
|
||||
|
||||
public function getBlockIdColumn(int $x, int $z) : string{
|
||||
return "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
}
|
||||
|
||||
public function getBlockDataColumn(int $x, int $z) : string{
|
||||
return "\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
}
|
||||
|
||||
public function getBlockLightColumn(int $x, int $z) : string{
|
||||
return "\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
}
|
||||
|
||||
public function getSkyLightColumn(int $x, int $z) : string{
|
||||
return "\xff\xff\xff\xff\xff\xff\xff\xff";
|
||||
}
|
||||
|
||||
public function getBlockIdArray() : string{
|
||||
return str_repeat("\x00", 4096);
|
||||
}
|
||||
|
||||
public function getBlockDataArray() : string{
|
||||
return str_repeat("\x00", 2048);
|
||||
}
|
||||
|
||||
public function getBlockLightArray() : string{
|
||||
return str_repeat("\x00", 2048);
|
||||
}
|
||||
|
||||
public function getSkyLightArray() : string{
|
||||
return str_repeat("\xff", 2048);
|
||||
}
|
||||
}
|
757
src/pocketmine/level/format/generic/GenericChunk.php
Normal file
757
src/pocketmine/level/format/generic/GenericChunk.php
Normal file
@ -0,0 +1,757 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implementation of MCPE-style chunks with subchunks with XZY ordering.
|
||||
*/
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace pocketmine\level\format\generic;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\tile\Tile;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
|
||||
class GenericChunk implements Chunk{
|
||||
|
||||
/** @var LevelProvider */
|
||||
protected $provider;
|
||||
|
||||
protected $x;
|
||||
protected $z;
|
||||
|
||||
protected $hasChanged = false;
|
||||
|
||||
protected $isInit = false;
|
||||
|
||||
protected $lightPopulated = false;
|
||||
protected $terrainGenerated = false;
|
||||
protected $terrainPopulated = false;
|
||||
|
||||
protected $height = 16;
|
||||
|
||||
/** @var SubChunk[] */
|
||||
protected $subChunks = [];
|
||||
|
||||
/** @var Tile[] */
|
||||
protected $tiles = [];
|
||||
protected $tileList = [];
|
||||
|
||||
/** @var Entity[] */
|
||||
protected $entities = [];
|
||||
|
||||
/** @var int[256] */
|
||||
protected $heightMap = [];
|
||||
|
||||
/** @var int[256] */
|
||||
protected $biomeColors = [];
|
||||
|
||||
protected $extraData = [];
|
||||
|
||||
/** @var CompoundTag[] */
|
||||
protected $NBTtiles = [];
|
||||
|
||||
/** @var CompoundTag[] */
|
||||
protected $NBTentities = [];
|
||||
|
||||
/**
|
||||
* @param LevelProvider $provider
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param SubChunk[] $subChunks
|
||||
* @param CompoundTag[] $entities
|
||||
* @param CompoundTag[] $tiles
|
||||
* @param int[256] $biomeColors
|
||||
* @param int[256] $heightMap
|
||||
*/
|
||||
public function __construct($provider, int $chunkX, int $chunkZ, array $subChunks = [], array $entities = [], array $tiles = [], array $biomeColors = [], array $heightMap = []){
|
||||
$this->provider = $provider;
|
||||
$this->x = $chunkX;
|
||||
$this->z = $chunkZ;
|
||||
|
||||
$this->height = $provider !== null ? ($provider->getWorldHeight() >> 4) : 16;
|
||||
|
||||
foreach($subChunks as $subChunk){
|
||||
$y = $subChunk->getY();
|
||||
if($y < 0 or $y >= $this->height){
|
||||
throw new ChunkException("Invalid subchunk index $y!");
|
||||
}
|
||||
if($subChunk->isEmpty()){
|
||||
$this->subChunks[$y] = new EmptySubChunk($y);
|
||||
}else{
|
||||
$this->subChunks[$y] = $subChunk;
|
||||
}
|
||||
}
|
||||
|
||||
for($i = 0; $i < $this->height; ++$i){
|
||||
if(!isset($this->subChunks[$i])){
|
||||
$this->subChunks[$i] = new EmptySubChunk($i);
|
||||
}
|
||||
}
|
||||
|
||||
if(count($heightMap) === 256){
|
||||
$this->heightMap = $heightMap;
|
||||
}else{
|
||||
assert(count($heightMap) === 0, "Wrong HeightMap value count, expected 256, got " . count($heightMap));
|
||||
$this->heightMap = array_fill(0, 256, 0);
|
||||
}
|
||||
|
||||
if(count($biomeColors) === 256){
|
||||
$this->biomeColors = $biomeColors;
|
||||
}else{
|
||||
assert(count($biomeColors) === 0, "Wrong HeightMap value count, expected 256, got " . count($biomeColors));
|
||||
$this->biomeColors = array_fill(0, 256, 0);
|
||||
}
|
||||
|
||||
$this->NBTtiles = $tiles;
|
||||
$this->NBTentities = $entities;
|
||||
}
|
||||
|
||||
public function getX() : int{
|
||||
return $this->x;
|
||||
}
|
||||
|
||||
public function getZ() : int{
|
||||
return $this->z;
|
||||
}
|
||||
|
||||
public function setX(int $x){
|
||||
$this->x = $x;
|
||||
}
|
||||
|
||||
public function setZ(int $z){
|
||||
$this->z = $z;
|
||||
}
|
||||
|
||||
public function getProvider(){
|
||||
return $this->provider;
|
||||
}
|
||||
|
||||
public function setProvider(LevelProvider $provider){
|
||||
$this->provider = $provider;
|
||||
}
|
||||
|
||||
public function getHeight() : int{
|
||||
return $this->height;
|
||||
}
|
||||
|
||||
public function getFullBlock(int $x, int $y, int $z) : int{
|
||||
return $this->getSubChunk($y >> 4)->getFullBlock($x, $y & 0x0f, $z);
|
||||
}
|
||||
|
||||
public function setBlock(int $x, int $y, int $z, $blockId = null, $meta = null){
|
||||
$this->getSubChunk($y >> 4, true)->setBlock($x, $y & 0x0f, $z, $blockId !== null ? ($blockId & 0xff) : null, $meta !== null ? ($meta & 0x0f) : null);
|
||||
}
|
||||
|
||||
public function getBlockId(int $x, int $y, int $z) : int{
|
||||
return $this->getSubChunk($y >> 4)->getBlockId($x, $y & 0x0f, $z);
|
||||
}
|
||||
|
||||
public function setBlockId(int $x, int $y, int $z, int $id){
|
||||
$this->getSubChunk($y >> 4, true)->setBlockId($x, $y & 0x0f, $z, $id);
|
||||
}
|
||||
|
||||
public function getBlockData(int $x, int $y, int $z) : int{
|
||||
return $this->getSubChunk($y >> 4)->getBlockData($x, $y & 0x0f, $z);
|
||||
}
|
||||
|
||||
public function setBlockData(int $x, int $y, int $z, int $data){
|
||||
$this->getSubChunk($y >> 4, true)->setBlockData($x, $y & 0x0f, $z, $data);
|
||||
}
|
||||
|
||||
public function getBlockExtraData(int $x, int $y, int $z) : int{
|
||||
return $this->extraData[Level::chunkBlockHash($x, $y, $z)] ?? 0;
|
||||
}
|
||||
|
||||
public function setBlockExtraData(int $x, int $y, int $z, int $data){
|
||||
if($data === 0){
|
||||
unset($this->extraData[Level::chunkBlockHash($x, $y, $z)]);
|
||||
}else{
|
||||
$this->extraData[Level::chunkBlockHash($x, $y, $z)] = $data;
|
||||
}
|
||||
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
|
||||
public function getBlockSkyLight(int $x, int $y, int $z) : int{
|
||||
return $this->getSubChunk($y >> 4)->getBlockSkyLight($x, $y & 0x0f, $z);
|
||||
}
|
||||
|
||||
public function setBlockSkyLight(int $x, int $y, int $z, int $level){
|
||||
$this->getSubChunk($y >> 4, true)->setBlockSkyLight($x, $y & 0x0f, $z, $level);
|
||||
}
|
||||
|
||||
public function getBlockLight(int $x, int $y, int $z) : int{
|
||||
return $this->getSubChunk($y >> 4)->getBlockLight($x, $y & 0x0f, $z);
|
||||
}
|
||||
|
||||
public function setBlockLight(int $x, int $y, int $z, int $level){
|
||||
$this->getSubChunk($y >> 4, true)->setBlockLight($x, $y & 0x0f, $z, $level);
|
||||
}
|
||||
|
||||
public function getHighestBlockAt(int $x, int $z, bool $useHeightMap = true) : int{
|
||||
if($useHeightMap){
|
||||
$height = $this->getHeightMap($x, $z);
|
||||
|
||||
if($height !== 0 and $height !== 255){
|
||||
return $height;
|
||||
}
|
||||
}
|
||||
|
||||
$index = $this->getHighestSubChunkIndex();
|
||||
if($index < 0){
|
||||
return 0;
|
||||
}
|
||||
|
||||
$height = $index << 4;
|
||||
|
||||
for($y = $index; $y >= 0; --$y){
|
||||
$height = $this->getSubChunk($y)->getHighestBlockAt($x, $z) | ($y << 4);
|
||||
if($height !== -1){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->setHeightMap($x, $z, $height);
|
||||
return $height;
|
||||
}
|
||||
|
||||
public function getHeightMap(int $x, int $z) : int{
|
||||
return $this->heightMap[($z << 4) | $x];
|
||||
}
|
||||
|
||||
public function setHeightMap(int $x, int $z, int $value){
|
||||
$this->heightMap[($z << 4) | $x] = $value;
|
||||
}
|
||||
|
||||
public function recalculateHeightMap(){
|
||||
for($z = 0; $z < 16; ++$z){
|
||||
for($x = 0; $x < 16; ++$x){
|
||||
$this->setHeightMap($x, $z, $this->getHighestBlockAt($x, $z, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function populateSkyLight(){
|
||||
for($x = 0; $x < 16; ++$x){
|
||||
for($z = 0; $z < 16; ++$z){
|
||||
$top = $this->getHeightMap($x, $z);
|
||||
for($y = 127; $y > $top; --$y){
|
||||
$this->setBlockSkyLight($x, $y, $z, 15);
|
||||
}
|
||||
|
||||
for($y = $top; $y >= 0; --$y){
|
||||
if(Block::$solid[$this->getBlockId($x, $y, $z)]){
|
||||
break;
|
||||
}
|
||||
|
||||
$this->setBlockSkyLight($x, $y, $z, 15);
|
||||
}
|
||||
|
||||
$this->setHeightMap($x, $z, $this->getHighestBlockAt($x, $z, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getBiomeId(int $x, int $z) : int{
|
||||
return ($this->biomeColors[($z << 4) | $x] & 0xFF000000) >> 24;
|
||||
}
|
||||
|
||||
public function setBiomeId(int $x, int $z, int $biomeId){
|
||||
$this->hasChanged = true;
|
||||
$this->biomeColors[($z << 4) | $x] = ($this->biomeColors[($z << 4) | $x] & 0xFFFFFF) | ($biomeId << 24);
|
||||
}
|
||||
|
||||
public function getBiomeColor(int $x, int $z) : int{
|
||||
$color = $this->biomeColors[($z << 4) | $x] & 0xFFFFFF;
|
||||
|
||||
return [$color >> 16, ($color >> 8) & 0xFF, $color & 0xFF];
|
||||
}
|
||||
|
||||
public function setBiomeColor(int $x, int $z, int $R, int $G, int $B){
|
||||
$this->hasChanged = true;
|
||||
$this->biomeColors[($z << 4) | $x] = ($this->biomeColors[($z << 4) | $x] & 0xFF000000) | (($R & 0xFF) << 16) | (($G & 0xFF) << 8) | ($B & 0xFF);
|
||||
}
|
||||
|
||||
public function getBlockIdColumn(int $x, int $z) : string{
|
||||
$result = "";
|
||||
foreach($this->subChunks as $subChunk){
|
||||
$result .= $subChunk->getBlockIdColumn($x, $z);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getBlockDataColumn(int $x, int $z) : string{
|
||||
$result = "";
|
||||
foreach($this->subChunks as $subChunk){
|
||||
$result .= $subChunk->getBlockDataColumn($x, $z);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getBlockSkyLightColumn(int $x, int $z) : string{
|
||||
$result = "";
|
||||
foreach($this->subChunks as $subChunk){
|
||||
$result .= $subChunk->getSkyLightColumn($x, $z);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getBlockLightColumn(int $x, int $z) : string{
|
||||
$result = "";
|
||||
foreach($this->subChunks as $subChunk){
|
||||
$result .= $subChunk->getBlockLightColumn($x, $z);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function isLightPopulated() : bool{
|
||||
return $this->lightPopulated;
|
||||
}
|
||||
|
||||
public function setLightPopulated(bool $value = true){
|
||||
$this->lightPopulated = $value;
|
||||
}
|
||||
|
||||
public function isPopulated() : bool{
|
||||
return $this->terrainPopulated;
|
||||
}
|
||||
|
||||
public function setPopulated(bool $value = true){
|
||||
$this->terrainPopulated = $value;
|
||||
}
|
||||
|
||||
public function isGenerated() : bool{
|
||||
return $this->terrainGenerated;
|
||||
}
|
||||
|
||||
public function setGenerated(bool $value = true){
|
||||
$this->terrainGenerated = $value;
|
||||
}
|
||||
|
||||
public function addEntity(Entity $entity){
|
||||
$this->entities[$entity->getId()] = $entity;
|
||||
if(!($entity instanceof Player) and $this->isInit){
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function removeEntity(Entity $entity){
|
||||
unset($this->entities[$entity->getId()]);
|
||||
if(!($entity instanceof Player) and $this->isInit){
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function addTile(Tile $tile){
|
||||
$this->tiles[$tile->getId()] = $tile;
|
||||
if(isset($this->tileList[$index = (($tile->x & 0x0f) << 12) | (($tile->z & 0x0f) << 8) | ($tile->y & 0xff)]) and $this->tileList[$index] !== $tile){
|
||||
$this->tileList[$index]->close();
|
||||
}
|
||||
$this->tileList[$index] = $tile;
|
||||
if($this->isInit){
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function removeTile(Tile $tile){
|
||||
unset($this->tiles[$tile->getId()]);
|
||||
unset($this->tileList[(($tile->x & 0x0f) << 12) | (($tile->z & 0x0f) << 8) | ($tile->y & 0xff)]);
|
||||
if($this->isInit){
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function getEntities() : array{
|
||||
return $this->entities;
|
||||
}
|
||||
|
||||
public function getTiles() : array{
|
||||
return $this->tiles;
|
||||
}
|
||||
|
||||
public function getTile(int $x, int $y, int $z){
|
||||
$index = ($x << 12) | ($z << 8) | $y;
|
||||
return $this->tileList[$index] ?? null;
|
||||
}
|
||||
|
||||
public function isLoaded() : bool{
|
||||
return $this->getProvider() === null ? false : $this->getProvider()->isChunkLoaded($this->getX(), $this->getZ());
|
||||
}
|
||||
|
||||
public function load(bool $generate = true) : bool{
|
||||
return $this->getProvider() === null ? false : $this->getProvider()->getChunk($this->getX(), $this->getZ(), true) instanceof GenericChunk;
|
||||
}
|
||||
|
||||
public function unload(bool $save = true, bool $safe = true) : bool{
|
||||
$level = $this->getProvider();
|
||||
if($level === null){
|
||||
return true;
|
||||
}
|
||||
if($save === true and $this->hasChanged){
|
||||
$level->saveChunk($this->getX(), $this->getZ());
|
||||
}
|
||||
if($safe === true){
|
||||
foreach($this->getEntities() as $entity){
|
||||
if($entity instanceof Player){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach($this->getEntities() as $entity){
|
||||
if($entity instanceof Player){
|
||||
continue;
|
||||
}
|
||||
$entity->close();
|
||||
}
|
||||
foreach($this->getTiles() as $tile){
|
||||
$tile->close();
|
||||
}
|
||||
$this->provider = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function initChunk(){
|
||||
if($this->getProvider() instanceof LevelProvider and !$this->isInit){
|
||||
$changed = false;
|
||||
if($this->NBTentities !== null){
|
||||
$this->getProvider()->getLevel()->timings->syncChunkLoadEntitiesTimer->startTiming();
|
||||
foreach($this->NBTentities as $nbt){
|
||||
if($nbt instanceof CompoundTag){
|
||||
if(!isset($nbt->id)){
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(($nbt["Pos"][0] >> 4) !== $this->x or ($nbt["Pos"][2] >> 4) !== $this->z){
|
||||
$changed = true;
|
||||
continue; //Fixes entities allocated in wrong chunks.
|
||||
}
|
||||
|
||||
if(($entity = Entity::createEntity($nbt["id"], $this, $nbt)) instanceof Entity){
|
||||
$entity->spawnToAll();
|
||||
}else{
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->getProvider()->getLevel()->timings->syncChunkLoadEntitiesTimer->stopTiming();
|
||||
|
||||
$this->getProvider()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->startTiming();
|
||||
foreach($this->NBTtiles as $nbt){
|
||||
if($nbt instanceof CompoundTag){
|
||||
if(!isset($nbt->id)){
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(($nbt["x"] >> 4) !== $this->x or ($nbt["z"] >> 4) !== $this->z){
|
||||
$changed = true;
|
||||
continue; //Fixes tiles allocated in wrong chunks.
|
||||
}
|
||||
|
||||
if(Tile::createTile($nbt["id"], $this, $nbt) === null){
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->getProvider()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->stopTiming();
|
||||
|
||||
$this->NBTentities = null;
|
||||
$this->NBTtiles = null;
|
||||
}
|
||||
|
||||
$this->hasChanged = $changed;
|
||||
|
||||
$this->isInit = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function getBiomeIdArray() : string{
|
||||
$ids = str_repeat("\x00", 256);
|
||||
foreach($this->biomeColors as $i => $d){
|
||||
$ids{$i} = chr(($d & 0xFF000000) >> 24);
|
||||
}
|
||||
return $ids;
|
||||
}
|
||||
|
||||
public function getBiomeColorArray() : array{
|
||||
return $this->biomeColors;
|
||||
}
|
||||
|
||||
public function getHeightMapArray() : array{
|
||||
return $this->heightMap;
|
||||
}
|
||||
|
||||
public function getBlockIdArray() : string{
|
||||
$result = "";
|
||||
foreach($this->subChunks as $subChunk){
|
||||
$result .= $subChunk->getBlockIdArray();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getBlockDataArray() : string{
|
||||
$result = "";
|
||||
foreach($this->subChunks as $subChunk){
|
||||
$result .= $subChunk->getBlockDataArray();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getBlockExtraDataArray() : array{
|
||||
return $this->extraData;
|
||||
}
|
||||
|
||||
public function getBlockSkyLightArray() : string{
|
||||
$result = "";
|
||||
foreach($this->subChunks as $subChunk){
|
||||
$result .= $subChunk->getSkyLightArray();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getBlockLightArray() : string{
|
||||
$result = "";
|
||||
foreach($this->subChunks as $subChunk){
|
||||
$result .= $subChunk->getBlockLightArray();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function hasChanged() : bool{
|
||||
return $this->hasChanged;
|
||||
}
|
||||
|
||||
public function setChanged(bool $value = true){
|
||||
$this->hasChanged = $value;
|
||||
}
|
||||
|
||||
public function getSubChunk(int $fY, bool $generateNew = false) : SubChunk{
|
||||
if($fY < 0 or $fY >= self::MAX_SUBCHUNKS){
|
||||
return new EmptySubChunk($fY);
|
||||
}elseif($generateNew and $this->subChunks[$fY] instanceof EmptySubChunk){
|
||||
$this->subChunks[$fY] = new SubChunk($fY);
|
||||
}
|
||||
return $this->subChunks[$fY];
|
||||
}
|
||||
|
||||
public function setSubChunk(int $fY, SubChunk $subChunk = null, bool $allowEmpty = false) : bool{
|
||||
if($fY < 0 or $fY >= self::MAX_SUBCHUNKS){
|
||||
return false;
|
||||
}
|
||||
if($subChunk === null or ($subChunk->isEmpty() and !$allowEmpty)){
|
||||
$this->subChunks[$fY] = new EmptySubChunk();
|
||||
}else{
|
||||
$this->subChunks[$fY] = $subChunk;
|
||||
}
|
||||
$this->hasChanged = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getSubChunks() : array{
|
||||
return $this->subChunks;
|
||||
}
|
||||
|
||||
public function getHighestSubChunkIndex() : int{
|
||||
for($y = count($this->subChunks) - 1; $y >= 0; --$y){
|
||||
if($this->subChunks[$y] === null or $this->subChunks[$y]->isEmpty()){
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $y;
|
||||
}
|
||||
|
||||
public function getSubChunkSendCount() : int{
|
||||
return $this->getHighestSubChunkIndex() + 1;
|
||||
}
|
||||
|
||||
public function getNonEmptySubChunkCount() : int{
|
||||
$result = 0;
|
||||
foreach($this->subChunks as $subChunk){
|
||||
if($subChunk->isEmpty()){
|
||||
continue;
|
||||
}
|
||||
++$result;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function clearEmptySubChunks(){
|
||||
foreach($this->subChunks as $y => $subChunk){
|
||||
if($subChunk->isEmpty()){
|
||||
if($y < 0 or $y > self::MAX_SUBCHUNKS){
|
||||
assert(false, "Invalid subchunk index");
|
||||
unset($this->subChunks[$y]);
|
||||
}elseif($subChunk instanceof EmptySubChunk){
|
||||
continue;
|
||||
}else{
|
||||
$this->subChunks[$y] = new EmptySubChunk($y);
|
||||
}
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function networkSerialize() : string{
|
||||
$result = "";
|
||||
$subChunkCount = $this->getSubChunkSendCount();
|
||||
$result .= chr($subChunkCount);
|
||||
for($y = 0; $y < $subChunkCount; ++$y){
|
||||
$subChunk = $this->subChunks[$y];
|
||||
$result .= "\x00" //unknown byte!
|
||||
. $subChunk->getBlockIdArray()
|
||||
. $subChunk->getBlockDataArray()
|
||||
. $subChunk->getSkyLightArray()
|
||||
. $subChunk->getBlockLightArray();
|
||||
}
|
||||
|
||||
//TODO: heightmaps, tile data
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function fastSerialize(Chunk $chunk) : string{
|
||||
$stream = new BinaryStream();
|
||||
$stream->putInt($chunk->x);
|
||||
$stream->putInt($chunk->z);
|
||||
$count = 0;
|
||||
$subChunks = "";
|
||||
foreach($chunk->subChunks as $subChunk){
|
||||
if($subChunk->isEmpty()){
|
||||
continue;
|
||||
}
|
||||
++$count;
|
||||
$subChunks .= chr($subChunk->getY())
|
||||
. $subChunk->getBlockIdArray()
|
||||
. $subChunk->getBlockDataArray()
|
||||
. $subChunk->getSkyLightArray()
|
||||
. $subChunk->getBlockLightArray();
|
||||
}
|
||||
$stream->putByte($count);
|
||||
$stream->put($subChunks);
|
||||
$stream->put(pack("C*", ...$chunk->getHeightMapArray()) .
|
||||
pack("N*", ...$chunk->getBiomeColorArray()) .
|
||||
chr(($chunk->lightPopulated ? 1 << 2 : 0) | ($chunk->terrainPopulated ? 1 << 1 : 0) | ($chunk->terrainGenerated ? 1 : 0)));
|
||||
//TODO: tiles and entities
|
||||
return $stream->getBuffer();
|
||||
}
|
||||
|
||||
public static function fastDeserialize(string $data, LevelProvider $provider = null){
|
||||
$stream = new BinaryStream();
|
||||
$stream->setBuffer($data);
|
||||
$data = null;
|
||||
$x = $stream->getInt();
|
||||
$z = $stream->getInt();
|
||||
$subChunks = [];
|
||||
$count = $stream->getByte();
|
||||
for($y = 0; $y < $count; ++$y){
|
||||
$subChunks[] = new SubChunk(
|
||||
$stream->getByte(), //y
|
||||
$stream->get(4096), //blockIds
|
||||
$stream->get(2048), //blockData
|
||||
$stream->get(2048), //skyLight
|
||||
$stream->get(2048) //blockLight
|
||||
);
|
||||
}
|
||||
$heightMap = array_values(unpack("C*", $stream->get(256)));
|
||||
$biomeColors = array_values(unpack("N*", $stream->get(1024)));
|
||||
|
||||
$chunk = new GenericChunk($provider, $x, $z, $subChunks, $heightMap, $biomeColors);
|
||||
$flags = $stream->getByte();
|
||||
$chunk->lightPopulated = (bool) ($flags & 4);
|
||||
$chunk->terrainPopulated = (bool) ($flags & 2);
|
||||
$chunk->terrainGenerated = (bool) ($flags & 1);
|
||||
return $chunk;
|
||||
}
|
||||
|
||||
public static function getEmptyChunk(int $x, int $z, LevelProvider $provider = null) : Chunk{
|
||||
return new GenericChunk($provider, $x, $z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-orders a byte array (YZX -> XZY and vice versa)
|
||||
*
|
||||
* @param string $array length 4096
|
||||
*
|
||||
* @return string length 4096
|
||||
*/
|
||||
public static final function reorderByteArray(string $array) : string{
|
||||
$result = str_repeat("\x00", 4096);
|
||||
for($x = 0; $x < 16; ++$x){
|
||||
for($z = 0; $z < 16; ++$z){
|
||||
for($y = 0; $y < 16; ++$y){
|
||||
$result{($x << 8) | ($z << 4) | $y} = $array{($y << 8) | ($z << 4) | $x};
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-orders a nibble array (YZX -> XZY and vice versa)
|
||||
*
|
||||
* @param string $array length 2048
|
||||
*
|
||||
* @return string length 2048
|
||||
*/
|
||||
public static final function reorderNibbleArray(string $array) : string{
|
||||
$result = str_repeat("\x00", 2048);
|
||||
for($x = 0; $x < 16; ++$x){
|
||||
for($z = 0; $z < 16; ++$z){
|
||||
for($y = 0; $y < 16; ++$y){
|
||||
$inputIndex = (($y << 7) | ($z << 3) | ($x >> 1));
|
||||
$outputIndex = (($x << 7) | ($z << 3) | ($y >> 1));
|
||||
$current = ord($result{$outputIndex});
|
||||
$byte = ord($array{$inputIndex});
|
||||
|
||||
if(($y & 1) === 0){
|
||||
if(($x & 1) === 0){
|
||||
$current |= ($byte & 0x0f);
|
||||
}else{
|
||||
$current |= (($byte >> 4) & 0x0f);
|
||||
}
|
||||
}else{
|
||||
if(($x & 1) === 0){
|
||||
$current |= (($byte << 4) & 0xf0);
|
||||
}else{
|
||||
$current |= ($byte & 0xf0);
|
||||
}
|
||||
}
|
||||
$result{$outputIndex} = chr($current);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
205
src/pocketmine/level/format/generic/SubChunk.php
Normal file
205
src/pocketmine/level/format/generic/SubChunk.php
Normal file
@ -0,0 +1,205 @@
|
||||
<?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\generic;
|
||||
|
||||
class SubChunk{
|
||||
protected $y;
|
||||
|
||||
protected $ids;
|
||||
protected $data;
|
||||
protected $blockLight;
|
||||
protected $skyLight;
|
||||
|
||||
private static function assignData(&$target, $data, $length, $char = "\x00"){
|
||||
if(strlen($data) !== $length){
|
||||
assert($data === "", "Invalid non-zero length given, expected $length, got " . strlen($data));
|
||||
$target = str_repeat($char, $length);
|
||||
}else{
|
||||
$target = $data;
|
||||
}
|
||||
}
|
||||
|
||||
public function __construct(int $y, string $ids = "", string $data = "", string $blockLight = "", string $skyLight = ""){
|
||||
$this->y = $y;
|
||||
self::assignData($this->ids, $ids, 4096);
|
||||
self::assignData($this->data, $data, 2048);
|
||||
self::assignData($this->blockLight, $blockLight, 2048);
|
||||
self::assignData($this->skyLight, $skyLight, 2048, "\xff");
|
||||
}
|
||||
|
||||
public function getY() : int{
|
||||
return $this->y;
|
||||
}
|
||||
|
||||
public function isEmpty() : bool{
|
||||
assert(strlen($this->ids) === 4096, "Wrong length of ID array, expecting 4096 bytes, got " . strlen($this->ids));
|
||||
return substr_count($this->ids, "\x00") === 4096;
|
||||
}
|
||||
|
||||
public function getBlockId(int $x, int $y, int $z) : int{
|
||||
return ord($this->ids{($x << 8) | ($z << 4) | $y});
|
||||
}
|
||||
|
||||
public function setBlockId(int $x, int $y, int $z, int $id){
|
||||
$this->ids{($x << 8) | ($z << 4) | $y} = chr($id);
|
||||
}
|
||||
|
||||
public function getBlockData(int $x, int $y, int $z) : int{
|
||||
$m = ord($this->data{($x << 7) + ($z << 3) + ($y >> 1)});
|
||||
if(($y & 1) === 0){
|
||||
return $m & 0x0f;
|
||||
}else{
|
||||
return $m >> 4;
|
||||
}
|
||||
}
|
||||
|
||||
public function setBlockData(int $x, int $y, int $z, int $data){
|
||||
$i = ($x << 7) | ($z << 3) | ($y >> 1);
|
||||
$current = ord($this->data{$i});
|
||||
if(($y & 1) === 0){
|
||||
$this->data{$i} = chr(($current & 0xf0) | ($data & 0x0f));
|
||||
}else{
|
||||
$this->data{$i} = chr((($data & 0x0f) << 4) | ($current & 0x0f));
|
||||
}
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
|
||||
public function getFullBlock(int $x, int $y, int $z) : int{
|
||||
$i = ($x << 8) | ($z << 4) | $y;
|
||||
if(($y & 1) === 0){
|
||||
return (ord($this->ids{$i}) << 4) | (ord($this->data{$i >> 1}) & 0x0f);
|
||||
}else{
|
||||
return (ord($this->ids{$i}) << 4) | (ord($this->data{$i >> 1}) >> 4);
|
||||
}
|
||||
}
|
||||
|
||||
public function setBlock(int $x, int $y, int $z, $id = null, $data = null) : bool{
|
||||
$i = ($x << 8) | ($z << 4) | $y;
|
||||
$changed = false;
|
||||
if($id !== null){
|
||||
$block = chr($id);
|
||||
if($this->ids{$i} !== $block){
|
||||
$this->ids{$i} = $block;
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if($data !== null){
|
||||
$i >>= 1;
|
||||
$byte = ord($this->data{$i});
|
||||
if(($y & 1) === 0){
|
||||
$this->data{$i} = chr(($byte & 0xf0) | ($data & 0x0f));
|
||||
}else{
|
||||
$this->data{$i} = chr((($data & 0x0f) << 4) | ($byte & 0x0f));
|
||||
}
|
||||
if($this->data{$i} !== $byte){
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $changed;
|
||||
}
|
||||
|
||||
public function getBlockSkyLight(int $x, int $y, int $z) : int{
|
||||
$byte = ord($this->skyLight{($x << 7) + ($z << 3) + ($y >> 1)});
|
||||
if(($y & 1) === 0){
|
||||
return $byte & 0x0f;
|
||||
}else{
|
||||
return $byte >> 4;
|
||||
}
|
||||
}
|
||||
|
||||
public function setBlockSkyLight(int $x, int $y, int $z, int $level){
|
||||
$i = ($x << 7) + ($z << 3) + ($y >> 1);
|
||||
$byte = ord($this->skyLight{$i});
|
||||
if(($y & 1) === 0){
|
||||
$this->skyLight{$i} = chr(($byte & 0xf0) | ($level & 0x0f));
|
||||
}else{
|
||||
$this->skyLight{$i} = chr((($level & 0x0f) << 4) | ($byte & 0x0f));
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockLight(int $x, int $y, int $z) : int{
|
||||
$byte = ord($this->blockLight{($x << 7) + ($z << 3) + ($y >> 1)});
|
||||
if(($y & 1) === 0){
|
||||
return $byte & 0x0f;
|
||||
}else{
|
||||
return $byte >> 4;
|
||||
}
|
||||
}
|
||||
|
||||
public function setBlockLight(int $x, int $y, int $z, int $level){
|
||||
$i = ($x << 7) + ($z << 3) + ($y >> 1);
|
||||
$byte = ord($this->blockLight{$i});
|
||||
if(($y & 1) === 0){
|
||||
$this->blockLight{$i} = chr(($byte & 0xf0) | ($level & 0x0f));
|
||||
}else{
|
||||
$this->blockLight{$i} = chr((($level & 0x0f) << 4) | ($byte & 0x0f));
|
||||
}
|
||||
}
|
||||
|
||||
public function getHighestBlockAt(int $x, int $z) : int{
|
||||
for($y = 15; $y >= 0; --$y){
|
||||
if($this->ids{($x << 8) | ($z << 4) | $y} !== "\x00"){
|
||||
return $y;
|
||||
}
|
||||
}
|
||||
|
||||
return -1; //highest block not in this subchunk
|
||||
}
|
||||
|
||||
public function getBlockIdColumn(int $x, int $z) : string{
|
||||
return substr($this->ids, (($x << 8) | ($z << 4)), 16);
|
||||
}
|
||||
|
||||
public function getBlockDataColumn(int $x, int $z) : string{
|
||||
return substr($this->data, (($x << 7) | ($z << 3)), 8);
|
||||
}
|
||||
|
||||
public function getBlockLightColumn(int $x, int $z) : string{
|
||||
return substr($this->blockLight, (($x << 7) | ($z << 3)), 8);
|
||||
}
|
||||
|
||||
public function getSkyLightColumn(int $x, int $z) : string{
|
||||
return substr($this->skyLight, (($x << 7) | ($z << 3)), 8);
|
||||
}
|
||||
|
||||
public function getBlockIdArray() : string{
|
||||
assert(strlen($this->ids) === 4096, "Wrong length of ID array, expecting 4096 bytes, got " . strlen($this->ids));
|
||||
return $this->ids;
|
||||
}
|
||||
|
||||
public function getBlockDataArray() : string{
|
||||
assert(strlen($this->data) === 2048, "Wrong length of data array, expecting 2048 bytes, got " . strlen($this->data));
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function getBlockLightArray() : string{
|
||||
assert(strlen($this->blockLight) === 2048, "Wrong length of light array, expecting 2048 bytes, got " . strlen($this->blockLight));
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function getSkyLightArray() : string{
|
||||
assert(strlen($this->skyLight) === 2048, "Wrong length of skylight array, expecting 2048 bytes, got " . strlen($this->skyLight));
|
||||
return $this->data;
|
||||
}
|
||||
}
|
@ -21,14 +21,14 @@
|
||||
|
||||
namespace pocketmine\level\format\leveldb;
|
||||
|
||||
use pocketmine\level\format\generic\BaseFullChunk;
|
||||
use pocketmine\level\format\generic\BaseChunk;
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
|
||||
class Chunk extends BaseFullChunk{
|
||||
class Chunk extends BaseChunk{
|
||||
|
||||
const DATA_LENGTH = 16384 * (2 + 1 + 1 + 1) + 256 + 1024;
|
||||
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
namespace pocketmine\level\format\leveldb;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\format\generic\BaseLevelProvider;
|
||||
use pocketmine\level\generator\Generator;
|
||||
use pocketmine\level\Level;
|
||||
@ -259,7 +259,7 @@ class LevelDB extends BaseLevelProvider{
|
||||
|
||||
public function unloadChunk($x, $z, $safe = true){
|
||||
$chunk = isset($this->chunks[$index = Level::chunkHash($x, $z)]) ? $this->chunks[$index] : null;
|
||||
if($chunk instanceof FullChunk and $chunk->unload(false, $safe)){
|
||||
if($chunk instanceof Chunk and $chunk->unload(false, $safe)){
|
||||
unset($this->chunks[$index]);
|
||||
return true;
|
||||
}
|
||||
@ -302,7 +302,7 @@ class LevelDB extends BaseLevelProvider{
|
||||
return $this->db;
|
||||
}
|
||||
|
||||
public function setChunk($chunkX, $chunkZ, FullChunk $chunk){
|
||||
public function setChunk($chunkX, $chunkZ, Chunk $chunk){
|
||||
if(!($chunk instanceof Chunk)){
|
||||
throw new ChunkException("Invalid Chunk class");
|
||||
}
|
||||
@ -341,7 +341,7 @@ class LevelDB extends BaseLevelProvider{
|
||||
|
||||
public function isChunkPopulated($chunkX, $chunkZ){
|
||||
$chunk = $this->getChunk($chunkX, $chunkZ);
|
||||
if($chunk instanceof FullChunk){
|
||||
if($chunk instanceof Chunk){
|
||||
return $chunk->isPopulated();
|
||||
}else{
|
||||
return false;
|
||||
|
@ -1,462 +0,0 @@
|
||||
<?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\mcregion;
|
||||
|
||||
use pocketmine\level\format\generic\BaseFullChunk;
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\tag\ByteArrayTag;
|
||||
use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\IntArrayTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\nbt\tag\ListTag;
|
||||
use pocketmine\nbt\tag\LongTag;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
|
||||
class Chunk extends BaseFullChunk{
|
||||
|
||||
/** @var CompoundTag */
|
||||
protected $nbt;
|
||||
|
||||
public function __construct($level, CompoundTag $nbt = null){
|
||||
if($nbt === null){
|
||||
$this->provider = $level;
|
||||
$this->nbt = new CompoundTag("Level", []);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->nbt = $nbt;
|
||||
|
||||
if(isset($this->nbt->Entities) and $this->nbt->Entities instanceof ListTag){
|
||||
$this->nbt->Entities->setTagType(NBT::TAG_Compound);
|
||||
}else{
|
||||
$this->nbt->Entities = new ListTag("Entities", []);
|
||||
$this->nbt->Entities->setTagType(NBT::TAG_Compound);
|
||||
}
|
||||
|
||||
if(isset($this->nbt->TileEntities) and $this->nbt->TileEntities instanceof ListTag){
|
||||
$this->nbt->TileEntities->setTagType(NBT::TAG_Compound);
|
||||
}else{
|
||||
$this->nbt->TileEntities = new ListTag("TileEntities", []);
|
||||
$this->nbt->TileEntities->setTagType(NBT::TAG_Compound);
|
||||
}
|
||||
|
||||
if(isset($this->nbt->TileTicks) and $this->nbt->TileTicks instanceof ListTag){
|
||||
$this->nbt->TileTicks->setTagType(NBT::TAG_Compound);
|
||||
}else{
|
||||
$this->nbt->TileTicks = new ListTag("TileTicks", []);
|
||||
$this->nbt->TileTicks->setTagType(NBT::TAG_Compound);
|
||||
}
|
||||
|
||||
if(!isset($this->nbt->BiomeColors) or !($this->nbt->BiomeColors instanceof IntArrayTag)){
|
||||
$this->nbt->BiomeColors = new IntArrayTag("BiomeColors", array_fill(0, 256, 0));
|
||||
}
|
||||
|
||||
if(!isset($this->nbt->HeightMap) or !($this->nbt->HeightMap instanceof IntArrayTag)){
|
||||
$this->nbt->HeightMap = new IntArrayTag("HeightMap", array_fill(0, 256, 0));
|
||||
}
|
||||
|
||||
if(!isset($this->nbt->Blocks)){
|
||||
$this->nbt->Blocks = new ByteArrayTag("Blocks", str_repeat("\x00", 32768));
|
||||
}
|
||||
|
||||
if(!isset($this->nbt->Data)){
|
||||
$this->nbt->Data = new ByteArrayTag("Data", $half = str_repeat("\x00", 16384));
|
||||
$this->nbt->SkyLight = new ByteArrayTag("SkyLight", $half);
|
||||
$this->nbt->BlockLight = new ByteArrayTag("BlockLight", $half);
|
||||
}
|
||||
|
||||
$extraData = [];
|
||||
|
||||
if(!isset($this->nbt->ExtraData) or !($this->nbt->ExtraData instanceof ByteArrayTag)){
|
||||
$this->nbt->ExtraData = new ByteArrayTag("ExtraData", Binary::writeInt(0));
|
||||
}else{
|
||||
$stream = new BinaryStream($this->nbt->ExtraData->getValue());
|
||||
$count = $stream->getInt();
|
||||
for($i = 0; $i < $count; ++$i){
|
||||
$key = $stream->getInt();
|
||||
$extraData[$key] = $stream->getShort(false);
|
||||
}
|
||||
}
|
||||
|
||||
parent::__construct($level, $this->nbt["xPos"], $this->nbt["zPos"], $this->nbt->Blocks->getValue(), $this->nbt->Data->getValue(), $this->nbt->SkyLight->getValue(), $this->nbt->BlockLight->getValue(), $this->nbt->BiomeColors->getValue(), $this->nbt->HeightMap->getValue(), $this->nbt->Entities->getValue(), $this->nbt->TileEntities->getValue(), $extraData);
|
||||
|
||||
if(isset($this->nbt->Biomes)){
|
||||
$this->checkOldBiomes($this->nbt->Biomes->getValue());
|
||||
unset($this->nbt->Biomes);
|
||||
}
|
||||
|
||||
unset($this->nbt->Blocks);
|
||||
unset($this->nbt->Data);
|
||||
unset($this->nbt->SkyLight);
|
||||
unset($this->nbt->BlockLight);
|
||||
unset($this->nbt->BiomeColors);
|
||||
unset($this->nbt->HeightMap);
|
||||
unset($this->nbt->Biomes);
|
||||
}
|
||||
|
||||
public function getBlockId($x, $y, $z){
|
||||
return ord($this->blocks{($x << 11) | ($z << 7) | $y});
|
||||
}
|
||||
|
||||
public function setBlockId($x, $y, $z, $id){
|
||||
$this->blocks{($x << 11) | ($z << 7) | $y} = chr($id);
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
|
||||
public function getBlockData($x, $y, $z){
|
||||
$m = ord($this->data{($x << 10) | ($z << 6) | ($y >> 1)});
|
||||
if(($y & 1) === 0){
|
||||
return $m & 0x0F;
|
||||
}else{
|
||||
return $m >> 4;
|
||||
}
|
||||
}
|
||||
|
||||
public function setBlockData($x, $y, $z, $data){
|
||||
$i = ($x << 10) | ($z << 6) | ($y >> 1);
|
||||
$old_m = ord($this->data{$i});
|
||||
if(($y & 1) === 0){
|
||||
$this->data{$i} = chr(($old_m & 0xf0) | ($data & 0x0f));
|
||||
}else{
|
||||
$this->data{$i} = chr((($data & 0x0f) << 4) | ($old_m & 0x0f));
|
||||
}
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
|
||||
public function getFullBlock($x, $y, $z){
|
||||
$i = ($x << 11) | ($z << 7) | $y;
|
||||
if(($y & 1) === 0){
|
||||
return (ord($this->blocks{$i}) << 4) | (ord($this->data{$i >> 1}) & 0x0F);
|
||||
}else{
|
||||
return (ord($this->blocks{$i}) << 4) | (ord($this->data{$i >> 1}) >> 4);
|
||||
}
|
||||
}
|
||||
|
||||
public function setBlock($x, $y, $z, $blockId = null, $meta = null){
|
||||
$i = ($x << 11) | ($z << 7) | $y;
|
||||
|
||||
$changed = false;
|
||||
|
||||
if($blockId !== null){
|
||||
$blockId = chr($blockId);
|
||||
if($this->blocks{$i} !== $blockId){
|
||||
$this->blocks{$i} = $blockId;
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if($meta !== null){
|
||||
$i >>= 1;
|
||||
$old_m = ord($this->data{$i});
|
||||
if(($y & 1) === 0){
|
||||
$this->data{$i} = chr(($old_m & 0xf0) | ($meta & 0x0f));
|
||||
if(($old_m & 0x0f) !== $meta){
|
||||
$changed = true;
|
||||
}
|
||||
}else{
|
||||
$this->data{$i} = chr((($meta & 0x0f) << 4) | ($old_m & 0x0f));
|
||||
if((($old_m & 0xf0) >> 4) !== $meta){
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($changed){
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
|
||||
return $changed;
|
||||
}
|
||||
|
||||
public function getBlockSkyLight($x, $y, $z){
|
||||
$sl = ord($this->skyLight{($x << 10) | ($z << 6) | ($y >> 1)});
|
||||
if(($y & 1) === 0){
|
||||
return $sl & 0x0F;
|
||||
}else{
|
||||
return $sl >> 4;
|
||||
}
|
||||
}
|
||||
|
||||
public function setBlockSkyLight($x, $y, $z, $level){
|
||||
$i = ($x << 10) | ($z << 6) | ($y >> 1);
|
||||
$old_sl = ord($this->skyLight{$i});
|
||||
if(($y & 1) === 0){
|
||||
$this->skyLight{$i} = chr(($old_sl & 0xf0) | ($level & 0x0f));
|
||||
}else{
|
||||
$this->skyLight{$i} = chr((($level & 0x0f) << 4) | ($old_sl & 0x0f));
|
||||
}
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
|
||||
public function getBlockLight($x, $y, $z){
|
||||
$l = ord($this->blockLight{($x << 10) | ($z << 6) | ($y >> 1)});
|
||||
if(($y & 1) === 0){
|
||||
return $l & 0x0F;
|
||||
}else{
|
||||
return $l >> 4;
|
||||
}
|
||||
}
|
||||
|
||||
public function setBlockLight($x, $y, $z, $level){
|
||||
$i = ($x << 10) | ($z << 6) | ($y >> 1);
|
||||
$old_l = ord($this->blockLight{$i});
|
||||
if(($y & 1) === 0){
|
||||
$this->blockLight{$i} = chr(($old_l & 0xf0) | ($level & 0x0f));
|
||||
}else{
|
||||
$this->blockLight{$i} = chr((($level & 0x0f) << 4) | ($old_l & 0x0f));
|
||||
}
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
|
||||
public function getBlockIdColumn($x, $z){
|
||||
return substr($this->blocks, ($x << 11) + ($z << 7), 128);
|
||||
}
|
||||
|
||||
public function getBlockDataColumn($x, $z){
|
||||
return substr($this->data, ($x << 10) + ($z << 6), 64);
|
||||
}
|
||||
|
||||
public function getBlockSkyLightColumn($x, $z){
|
||||
return substr($this->skyLight, ($x << 10) + ($z << 6), 64);
|
||||
}
|
||||
|
||||
public function getBlockLightColumn($x, $z){
|
||||
return substr($this->blockLight, ($x << 10) + ($z << 6), 64);
|
||||
}
|
||||
|
||||
public function isLightPopulated(){
|
||||
return $this->nbt["LightPopulated"] > 0;
|
||||
}
|
||||
|
||||
public function setLightPopulated($value = 1){
|
||||
$this->nbt->LightPopulated = new ByteTag("LightPopulated", $value ? 1 : 0);
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isPopulated(){
|
||||
return isset($this->nbt->TerrainPopulated) and $this->nbt->TerrainPopulated->getValue() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $value
|
||||
*/
|
||||
public function setPopulated($value = 1){
|
||||
$this->nbt->TerrainPopulated = new ByteTag("TerrainPopulated", $value ? 1 : 0);
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isGenerated(){
|
||||
if(isset($this->nbt->TerrainGenerated)){
|
||||
return $this->nbt->TerrainGenerated->getValue() > 0;
|
||||
}elseif(isset($this->nbt->TerrainPopulated)){
|
||||
return $this->nbt->TerrainPopulated->getValue() > 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $value
|
||||
*/
|
||||
public function setGenerated($value = 1){
|
||||
$this->nbt->TerrainGenerated = new ByteTag("TerrainGenerated", (int) $value);
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
* @param LevelProvider $provider
|
||||
*
|
||||
* @return Chunk
|
||||
*/
|
||||
public static function fromBinary($data, LevelProvider $provider = null){
|
||||
$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)){
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Chunk($provider instanceof LevelProvider ? $provider : McRegion::class, $chunk->Level);
|
||||
}catch(\Throwable $e){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static function fromFastBinary($data, LevelProvider $provider = null){
|
||||
|
||||
try{
|
||||
$offset = 0;
|
||||
|
||||
$chunk = new Chunk($provider instanceof LevelProvider ? $provider : McRegion::class, null);
|
||||
$chunk->provider = $provider;
|
||||
$chunk->x = Binary::readInt(substr($data, $offset, 4));
|
||||
$offset += 4;
|
||||
$chunk->z = Binary::readInt(substr($data, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
$chunk->blocks = substr($data, $offset, 32768);
|
||||
$offset += 32768;
|
||||
$chunk->data = substr($data, $offset, 16384);
|
||||
$offset += 16384;
|
||||
$chunk->skyLight = substr($data, $offset, 16384);
|
||||
$offset += 16384;
|
||||
$chunk->blockLight = substr($data, $offset, 16384);
|
||||
$offset += 16384;
|
||||
|
||||
$chunk->heightMap = array_values(unpack("C*", substr($data, $offset, 256)));
|
||||
$offset += 256;
|
||||
$chunk->biomeColors = array_values(unpack("N*", substr($data, $offset, 1024)));
|
||||
$offset += 1024;
|
||||
|
||||
$flags = ord($data{$offset++});
|
||||
|
||||
$chunk->nbt->TerrainGenerated = new ByteTag("TerrainGenerated", $flags & 0b1);
|
||||
$chunk->nbt->TerrainPopulated = new ByteTag("TerrainPopulated", ($flags >> 1) & 0b1);
|
||||
$chunk->nbt->LightPopulated = new ByteTag("LightPopulated", ($flags >> 2) & 0b1);
|
||||
|
||||
return $chunk;
|
||||
}catch(\Throwable $e){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function toFastBinary(){
|
||||
return
|
||||
Binary::writeInt($this->x) .
|
||||
Binary::writeInt($this->z) .
|
||||
$this->getBlockIdArray() .
|
||||
$this->getBlockDataArray() .
|
||||
$this->getBlockSkyLightArray() .
|
||||
$this->getBlockLightArray() .
|
||||
pack("C*", ...$this->getHeightMapArray()) .
|
||||
pack("N*", ...$this->getBiomeColorArray()) .
|
||||
chr(($this->isLightPopulated() ? 1 << 2 : 0) + ($this->isPopulated() ? 1 << 1 : 0) + ($this->isGenerated() ? 1 : 0));
|
||||
}
|
||||
|
||||
public function toBinary(){
|
||||
$nbt = clone $this->getNBT();
|
||||
|
||||
$nbt->xPos = new IntTag("xPos", $this->x);
|
||||
$nbt->zPos = new IntTag("zPos", $this->z);
|
||||
|
||||
if($this->isGenerated()){
|
||||
$nbt->Blocks = new ByteArrayTag("Blocks", $this->getBlockIdArray());
|
||||
$nbt->Data = new ByteArrayTag("Data", $this->getBlockDataArray());
|
||||
$nbt->SkyLight = new ByteArrayTag("SkyLight", $this->getBlockSkyLightArray());
|
||||
$nbt->BlockLight = new ByteArrayTag("BlockLight", $this->getBlockLightArray());
|
||||
|
||||
$nbt->BiomeColors = new IntArrayTag("BiomeColors", $this->getBiomeColorArray());
|
||||
|
||||
$nbt->HeightMap = new IntArrayTag("HeightMap", $this->getHeightMapArray());
|
||||
}
|
||||
|
||||
$entities = [];
|
||||
|
||||
foreach($this->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($this->getTiles() as $tile){
|
||||
$tile->saveNBT();
|
||||
$tiles[] = $tile->namedtag;
|
||||
}
|
||||
|
||||
$nbt->TileEntities = new ListTag("TileEntities", $tiles);
|
||||
$nbt->TileEntities->setTagType(NBT::TAG_Compound);
|
||||
|
||||
$extraData = new BinaryStream();
|
||||
$extraData->putInt(count($this->getBlockExtraDataArray()));
|
||||
foreach($this->getBlockExtraDataArray() as $key => $value){
|
||||
$extraData->putInt($key);
|
||||
$extraData->putShort($value);
|
||||
}
|
||||
|
||||
$nbt->ExtraData = new ByteArrayTag("ExtraData", $extraData->getBuffer());
|
||||
|
||||
$writer = new NBT(NBT::BIG_ENDIAN);
|
||||
$nbt->setName("Level");
|
||||
$writer->setData(new CompoundTag("", ["Level" => $nbt]));
|
||||
|
||||
return $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CompoundTag
|
||||
*/
|
||||
public function getNBT(){
|
||||
return $this->nbt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param LevelProvider $provider
|
||||
*
|
||||
* @return Chunk
|
||||
*/
|
||||
public static function getEmptyChunk($chunkX, $chunkZ, LevelProvider $provider = null){
|
||||
try{
|
||||
$chunk = new Chunk($provider instanceof LevelProvider ? $provider : McRegion::class, null);
|
||||
$chunk->x = $chunkX;
|
||||
$chunk->z = $chunkZ;
|
||||
|
||||
$chunk->data = str_repeat("\x00", 16384);
|
||||
$chunk->blocks = $chunk->data . $chunk->data;
|
||||
$chunk->skyLight = str_repeat("\xff", 16384);
|
||||
$chunk->blockLight = $chunk->data;
|
||||
|
||||
$chunk->heightMap = array_fill(0, 256, 0);
|
||||
$chunk->biomeColors = array_fill(0, 256, 0);
|
||||
|
||||
$chunk->nbt->V = new ByteTag("V", 1);
|
||||
$chunk->nbt->InhabitedTime = new LongTag("InhabitedTime", 0);
|
||||
$chunk->nbt->TerrainGenerated = new ByteTag("TerrainGenerated", 0);
|
||||
$chunk->nbt->TerrainPopulated = new ByteTag("TerrainPopulated", 0);
|
||||
$chunk->nbt->LightPopulated = new ByteTag("LightPopulated", 0);
|
||||
|
||||
return $chunk;
|
||||
}catch(\Throwable $e){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -21,26 +21,157 @@
|
||||
|
||||
namespace pocketmine\level\format\mcregion;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
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\format\generic\BaseLevelProvider;
|
||||
use pocketmine\level\generator\Generator;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\nbt\tag\LongTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\tile\Spawnable;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
use pocketmine\nbt\tag\{ByteArrayTag, ByteTag, CompoundTag, IntArrayTag, IntTag, ListTag, LongTag, StringTag};
|
||||
use pocketmine\Player;
|
||||
use pocketmine\utils\ChunkException;
|
||||
|
||||
class McRegion extends BaseLevelProvider{
|
||||
|
||||
public static 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", 0); //guess
|
||||
$nbt->LastUpdate = new LongTag("LastUpdate", 0); //TODO
|
||||
$nbt->InhabitedTime = new LongTag("InhabitedTime", 0); //TODO
|
||||
$nbt->TerrainGenerated = new ByteTag("TerrainGenerated", $chunk->isGenerated());
|
||||
$nbt->TerrainPopulated = new ByteTag("TerrainPopulated", $chunk->isPopulated());
|
||||
$nbt->LightPopulated = new ByteTag("LightPopulated", $chunk->isLightPopulated());
|
||||
|
||||
$ids = "";
|
||||
$data = "";
|
||||
$blockLight = "";
|
||||
$skyLight = "";
|
||||
for($x = 0; $x < 16; ++$x){
|
||||
for($z = 0; $z < 16; ++$z){
|
||||
for($y = 0; $y < 8; ++$y){
|
||||
$subChunk = $chunk->getSubChunk($y);
|
||||
$ids .= $subChunk->getBlockIdColumn($x, $z);
|
||||
$data .= $subChunk->getBlockDataColumn($x, $z);
|
||||
$blockLight .= $subChunk->getBlockLightColumn($x, $z);
|
||||
$skyLight .= $subChunk->getSkyLightColumn($x, $z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$nbt->Blocks = new ByteArrayTag("Blocks", $ids);
|
||||
$nbt->Data = new ByteArrayTag("Data", $data);
|
||||
$nbt->SkyLight = new ByteArrayTag("SkyLight", $skyLight);
|
||||
$nbt->BlockLight = new ByteArrayTag("BlockLight", $blockLight);
|
||||
|
||||
$nbt->BiomeColors = new IntArrayTag("BiomeColors", $chunk->getBiomeColorArray());
|
||||
$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 static function nbtDeserialize(string $data, LevelProvider $provider = null){
|
||||
$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)){
|
||||
return null;
|
||||
}
|
||||
|
||||
$chunk = $chunk->Level;
|
||||
|
||||
$subChunks = [];
|
||||
$fullIds = $chunk->Blocks instanceof ByteArrayTag ? $chunk->Blocks->getValue() : str_repeat("\x00", 32768);
|
||||
$fullData = $chunk->Data instanceof ByteArrayTag ? $chunk->Data->getValue() : ($half = str_repeat("\x00", 16384));
|
||||
$fullBlockLight = $chunk->BlockLight instanceof ByteArrayTag ? $chunk->BlockLight->getValue() : $half;
|
||||
$fullSkyLight = $chunk->SkyLight instanceof ByteArrayTag ? $chunk->SkyLight->getValue() : str_repeat("\xff", 16384);
|
||||
|
||||
for($y = 0; $y < 8; ++$y){
|
||||
$offset = ($y << 4);
|
||||
$ids = "";
|
||||
for($i = 0; $i < 256; ++$i){
|
||||
$ids .= substr($fullIds, $offset, 16);
|
||||
$offset += 128;
|
||||
}
|
||||
$data = "";
|
||||
$offset = ($y << 3);
|
||||
for($i = 0; $i < 256; ++$i){
|
||||
$data .= substr($fullData, $offset, 8);
|
||||
$offset += 64;
|
||||
}
|
||||
$blockLight = "";
|
||||
$offset = ($y << 3);
|
||||
for($i = 0; $i < 256; ++$i){
|
||||
$blockLight .= substr($fullBlockLight, $offset, 8);
|
||||
$offset += 64;
|
||||
}
|
||||
$skyLight = "";
|
||||
$offset = ($y << 3);
|
||||
for($i = 0; $i < 256; ++$i){
|
||||
$skyLight .= substr($fullSkyLight, $offset, 8);
|
||||
$offset += 64;
|
||||
}
|
||||
$subChunks[] = new SubChunk($y, $ids, $data, $blockLight, $skyLight);
|
||||
}
|
||||
|
||||
$result = new GenericChunk(
|
||||
$provider,
|
||||
$chunk["xPos"],
|
||||
$chunk["zPos"],
|
||||
$subChunks,
|
||||
$chunk->Entities instanceof ListTag ? $chunk->Entities->getValue() : [],
|
||||
$chunk->TileEntities instanceof ListTag ? $chunk->TileEntities->getValue() : [],
|
||||
$chunk->BiomeColors instanceof IntArrayTag ? $chunk->BiomeColors->getValue() : [],
|
||||
$chunk->HeightMap instanceof IntArrayTag ? $chunk->HeightMap->getValue() : []
|
||||
);
|
||||
$result->setLightPopulated($chunk->LightPopulated instanceof ByteTag ? ((bool) $chunk->LightPopulated->getValue()) : false);
|
||||
$result->setPopulated($chunk->TerrainPopulated instanceof ByteTag ? ((bool) $chunk->TerrainPopulated->getValue()) : false);
|
||||
$result->setGenerated($chunk->TerrainGenerated instanceof ByteTag ? ((bool) $chunk->TerrainGenerated->getValue()) : false);
|
||||
return $result;
|
||||
}catch(\Throwable $e){
|
||||
echo $e->getMessage();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** @var RegionLoader[] */
|
||||
protected $regions = [];
|
||||
|
||||
/** @var Chunk[] */
|
||||
/** @var GenericChunk[] */
|
||||
protected $chunks = [];
|
||||
|
||||
public static function getProviderName(){
|
||||
@ -51,10 +182,6 @@ class McRegion extends BaseLevelProvider{
|
||||
return self::ORDER_ZXY;
|
||||
}
|
||||
|
||||
public static function usesChunkSection(){
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function isValid($path){
|
||||
$isValid = (file_exists($path . "/level.dat") and is_dir($path . "/region/"));
|
||||
|
||||
@ -71,6 +198,11 @@ class McRegion extends BaseLevelProvider{
|
||||
return $isValid;
|
||||
}
|
||||
|
||||
public function getWorldHeight() : int{
|
||||
//TODO: add world height options
|
||||
return 128;
|
||||
}
|
||||
|
||||
public static function generate($path, $name, $seed, $generator, array $options = []){
|
||||
if(!file_exists($path)){
|
||||
mkdir($path, 0777, true);
|
||||
@ -112,47 +244,6 @@ class McRegion extends BaseLevelProvider{
|
||||
$z = $chunkZ >> 5;
|
||||
}
|
||||
|
||||
public function requestChunkTask($x, $z){
|
||||
$chunk = $this->getChunk($x, $z, false);
|
||||
if(!($chunk instanceof Chunk)){
|
||||
throw new ChunkException("Invalid Chunk sent");
|
||||
}
|
||||
|
||||
$tiles = "";
|
||||
|
||||
if(count($chunk->getTiles()) > 0){
|
||||
$nbt = new NBT(NBT::LITTLE_ENDIAN);
|
||||
$list = [];
|
||||
foreach($chunk->getTiles() as $tile){
|
||||
if($tile instanceof Spawnable){
|
||||
$list[] = $tile->getSpawnCompound();
|
||||
}
|
||||
}
|
||||
$nbt->setData($list);
|
||||
$tiles = $nbt->write(true);
|
||||
}
|
||||
|
||||
$extraData = new BinaryStream();
|
||||
$extraData->putLInt(count($chunk->getBlockExtraDataArray()));
|
||||
foreach($chunk->getBlockExtraDataArray() as $key => $value){
|
||||
$extraData->putLInt($key);
|
||||
$extraData->putLShort($value);
|
||||
}
|
||||
|
||||
$ordered = $chunk->getBlockIdArray() .
|
||||
$chunk->getBlockDataArray() .
|
||||
$chunk->getBlockSkyLightArray() .
|
||||
$chunk->getBlockLightArray() .
|
||||
pack("C*", ...$chunk->getHeightMapArray()) .
|
||||
pack("N*", ...$chunk->getBiomeColorArray()) .
|
||||
$extraData->getBuffer() .
|
||||
$tiles;
|
||||
|
||||
$this->getLevel()->chunkRequestCallback($x, $z, $ordered);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function unloadChunks(){
|
||||
foreach($this->chunks as $chunk){
|
||||
$this->unloadChunk($chunk->getX(), $chunk->getZ(), false);
|
||||
@ -216,12 +307,12 @@ class McRegion extends BaseLevelProvider{
|
||||
}
|
||||
|
||||
public function getEmptyChunk($chunkX, $chunkZ){
|
||||
return Chunk::getEmptyChunk($chunkX, $chunkZ, $this);
|
||||
return GenericChunk::getEmptyChunk($chunkX, $chunkZ, $this);
|
||||
}
|
||||
|
||||
public function unloadChunk($x, $z, $safe = true){
|
||||
$chunk = isset($this->chunks[$index = Level::chunkHash($x, $z)]) ? $this->chunks[$index] : null;
|
||||
if($chunk instanceof FullChunk and $chunk->unload(false, $safe)){
|
||||
if($chunk instanceof Chunk and $chunk->unload(false, $safe)){
|
||||
unset($this->chunks[$index]);
|
||||
return true;
|
||||
}
|
||||
@ -246,7 +337,7 @@ class McRegion extends BaseLevelProvider{
|
||||
* @return RegionLoader
|
||||
*/
|
||||
protected function getRegion($x, $z){
|
||||
return isset($this->regions[$index = Level::chunkHash($x, $z)]) ? $this->regions[$index] : null;
|
||||
return $this->regions[Level::chunkHash($x, $z)] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -267,8 +358,8 @@ class McRegion extends BaseLevelProvider{
|
||||
}
|
||||
}
|
||||
|
||||
public function setChunk($chunkX, $chunkZ, FullChunk $chunk){
|
||||
if(!($chunk instanceof Chunk)){
|
||||
public function setChunk($chunkX, $chunkZ, Chunk $chunk){
|
||||
if(!($chunk instanceof GenericChunk)){
|
||||
throw new ChunkException("Invalid Chunk class");
|
||||
}
|
||||
|
||||
@ -288,10 +379,6 @@ class McRegion extends BaseLevelProvider{
|
||||
$this->chunks[$index] = $chunk;
|
||||
}
|
||||
|
||||
public static function createChunkSection($Y){
|
||||
return null;
|
||||
}
|
||||
|
||||
public function isChunkGenerated($chunkX, $chunkZ){
|
||||
if(($region = $this->getRegion($chunkX >> 5, $chunkZ >> 5)) !== null){
|
||||
return $region->chunkExists($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32) and $this->getChunk($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32, true)->isGenerated();
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
namespace pocketmine\level\format\mcregion;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\generic\GenericChunk;
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\ChunkException;
|
||||
@ -112,7 +112,7 @@ class RegionLoader{
|
||||
}
|
||||
|
||||
$chunk = $this->unserializeChunk(fread($this->filePointer, $length - 1));
|
||||
if($chunk instanceof FullChunk){
|
||||
if($chunk instanceof GenericChunk){
|
||||
return $chunk;
|
||||
}else{
|
||||
MainLogger::getLogger()->error("Corrupted chunk detected");
|
||||
@ -121,7 +121,7 @@ class RegionLoader{
|
||||
}
|
||||
|
||||
protected function unserializeChunk($data){
|
||||
return Chunk::fromBinary($data, $this->levelProvider);
|
||||
return McRegion::nbtDeserialize($data, $this->levelProvider);
|
||||
}
|
||||
|
||||
public function chunkExists($x, $z){
|
||||
@ -161,9 +161,9 @@ class RegionLoader{
|
||||
$this->locationTable[$index][1] = 0;
|
||||
}
|
||||
|
||||
public function writeChunk(FullChunk $chunk){
|
||||
public function writeChunk(GenericChunk $chunk){
|
||||
$this->lastUsed = time();
|
||||
$chunkData = $chunk->toBinary();
|
||||
$chunkData = McRegion::nbtSerialize($chunk);
|
||||
if($chunkData !== false){
|
||||
$this->saveChunk($chunk->getX() - ($this->getX() * 32), $chunk->getZ() - ($this->getZ() * 32), $chunkData);
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ use pocketmine\block\LapisOre;
|
||||
use pocketmine\block\RedstoneOre;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\generator\biome\Biome;
|
||||
use pocketmine\level\generator\populator\Ore;
|
||||
use pocketmine\level\generator\populator\Populator;
|
||||
@ -41,7 +41,7 @@ use pocketmine\utils\Random;
|
||||
class Flat extends Generator{
|
||||
/** @var ChunkManager */
|
||||
private $level;
|
||||
/** @var FullChunk */
|
||||
/** @var Chunk */
|
||||
private $chunk;
|
||||
/** @var Random */
|
||||
private $random;
|
||||
|
@ -21,7 +21,8 @@
|
||||
|
||||
namespace pocketmine\level\generator;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\format\generic\GenericChunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\level\SimpleChunkManager;
|
||||
use pocketmine\scheduler\AsyncTask;
|
||||
@ -34,10 +35,10 @@ class GenerationTask extends AsyncTask{
|
||||
public $chunk;
|
||||
public $chunkClass;
|
||||
|
||||
public function __construct(Level $level, FullChunk $chunk){
|
||||
public function __construct(Level $level, Chunk $chunk){
|
||||
$this->state = true;
|
||||
$this->levelId = $level->getId();
|
||||
$this->chunk = $chunk->toFastBinary();
|
||||
$this->chunk = GenericChunk::fastSerialize($chunk);
|
||||
$this->chunkClass = get_class($chunk);
|
||||
}
|
||||
|
||||
@ -51,9 +52,9 @@ class GenerationTask extends AsyncTask{
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var FullChunk $chunk */
|
||||
/** @var Chunk $chunk */
|
||||
$chunk = $this->chunkClass;
|
||||
$chunk = $chunk::fromFastBinary($this->chunk);
|
||||
$chunk = GenericChunk::fastDeserialize($this->chunk);
|
||||
if($chunk === null){
|
||||
//TODO error
|
||||
return;
|
||||
@ -65,7 +66,7 @@ class GenerationTask extends AsyncTask{
|
||||
|
||||
$chunk = $manager->getChunk($chunk->getX(), $chunk->getZ());
|
||||
$chunk->setGenerated();
|
||||
$this->chunk = $chunk->toFastBinary();
|
||||
$this->chunk = GenericChunk::fastSerialize($chunk);
|
||||
|
||||
$manager->setChunk($chunk->getX(), $chunk->getZ(), null);
|
||||
}
|
||||
@ -77,9 +78,9 @@ class GenerationTask extends AsyncTask{
|
||||
$level->registerGenerator();
|
||||
return;
|
||||
}
|
||||
/** @var FullChunk $chunk */
|
||||
/** @var Chunk $chunk */
|
||||
$chunk = $this->chunkClass;
|
||||
$chunk = $chunk::fromFastBinary($this->chunk, $level->getProvider());
|
||||
$chunk = GenericChunk::fastDeserialize($this->chunk, $level->getProvider());
|
||||
if($chunk === null){
|
||||
//TODO error
|
||||
return;
|
||||
|
@ -21,7 +21,8 @@
|
||||
|
||||
namespace pocketmine\level\generator;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\format\generic\FullChunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\scheduler\AsyncTask;
|
||||
use pocketmine\Server;
|
||||
@ -32,16 +33,16 @@ class LightPopulationTask extends AsyncTask{
|
||||
public $chunk;
|
||||
public $chunkClass;
|
||||
|
||||
public function __construct(Level $level, FullChunk $chunk){
|
||||
public function __construct(Level $level, Chunk $chunk){
|
||||
$this->levelId = $level->getId();
|
||||
$this->chunk = $chunk->toFastBinary();
|
||||
$this->chunk = GenericChunk::fastDeserialize($this->chunk);
|
||||
$this->chunkClass = get_class($chunk);
|
||||
}
|
||||
|
||||
public function onRun(){
|
||||
/** @var FullChunk $chunk */
|
||||
/** @var Chunk $chunk */
|
||||
$chunk = $this->chunkClass;
|
||||
$chunk = $chunk::fromFastBinary($this->chunk);
|
||||
$chunk = GenericChunk::fastDeserialize($this->chunk);
|
||||
if($chunk === null){
|
||||
//TODO error
|
||||
return;
|
||||
@ -51,15 +52,15 @@ class LightPopulationTask extends AsyncTask{
|
||||
$chunk->populateSkyLight();
|
||||
$chunk->setLightPopulated();
|
||||
|
||||
$this->chunk = $chunk->toFastBinary();
|
||||
$this->chunk = GenericChunk::fastSerialize($chunk);
|
||||
}
|
||||
|
||||
public function onCompletion(Server $server){
|
||||
$level = $server->getLevel($this->levelId);
|
||||
if($level !== null){
|
||||
/** @var FullChunk $chunk */
|
||||
/** @var Chunk $chunk */
|
||||
$chunk = $this->chunkClass;
|
||||
$chunk = $chunk::fromFastBinary($this->chunk, $level->getProvider());
|
||||
$chunk = GenericChunk::fastDeserialize($this->chunk, $level->getProvider());
|
||||
if($chunk === null){
|
||||
//TODO error
|
||||
return;
|
||||
|
@ -21,7 +21,8 @@
|
||||
|
||||
namespace pocketmine\level\generator;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\format\generic\GenericChunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\level\SimpleChunkManager;
|
||||
use pocketmine\scheduler\AsyncTask;
|
||||
@ -29,11 +30,9 @@ use pocketmine\Server;
|
||||
|
||||
class PopulationTask extends AsyncTask{
|
||||
|
||||
|
||||
public $state;
|
||||
public $levelId;
|
||||
public $chunk;
|
||||
public $chunkClass;
|
||||
|
||||
public $chunk0;
|
||||
public $chunk1;
|
||||
@ -45,11 +44,10 @@ class PopulationTask extends AsyncTask{
|
||||
public $chunk7;
|
||||
public $chunk8;
|
||||
|
||||
public function __construct(Level $level, FullChunk $chunk){
|
||||
public function __construct(Level $level, Chunk $chunk){
|
||||
$this->state = true;
|
||||
$this->levelId = $level->getId();
|
||||
$this->chunk = $chunk->toFastBinary();
|
||||
$this->chunkClass = get_class($chunk);
|
||||
$this->chunk = GenericChunk::fastSerialize($chunk);
|
||||
|
||||
for($i = 0; $i < 9; ++$i){
|
||||
if($i === 4){
|
||||
@ -58,7 +56,7 @@ class PopulationTask extends AsyncTask{
|
||||
$xx = -1 + $i % 3;
|
||||
$zz = -1 + (int) ($i / 3);
|
||||
$ck = $level->getChunk($chunk->getX() + $xx, $chunk->getZ() + $zz, false);
|
||||
$this->{"chunk$i"} = $ck !== null ? $ck->toFastBinary() : null;
|
||||
$this->{"chunk$i"} = $ck !== null ? GenericChunk::fastSerialize($ck) : null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,12 +70,10 @@ class PopulationTask extends AsyncTask{
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var FullChunk[] $chunks */
|
||||
/** @var Chunk[] $chunks */
|
||||
$chunks = [];
|
||||
/** @var FullChunk $chunkC */
|
||||
$chunkC = $this->chunkClass;
|
||||
|
||||
$chunk = $chunkC::fromFastBinary($this->chunk);
|
||||
$chunk = GenericChunk::fastDeserialize($this->chunk);
|
||||
|
||||
for($i = 0; $i < 9; ++$i){
|
||||
if($i === 4){
|
||||
@ -87,9 +83,9 @@ class PopulationTask extends AsyncTask{
|
||||
$zz = -1 + (int) ($i / 3);
|
||||
$ck = $this->{"chunk$i"};
|
||||
if($ck === null){
|
||||
$chunks[$i] = $chunkC::getEmptyChunk($chunk->getX() + $xx, $chunk->getZ() + $zz);
|
||||
$chunks[$i] = GenericChunk::getEmptyChunk($chunk->getX() + $xx, $chunk->getZ() + $zz);
|
||||
}else{
|
||||
$chunks[$i] = $chunkC::fromFastBinary($ck);
|
||||
$chunks[$i] = GenericChunk::fastDeserialize($ck);
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,7 +118,7 @@ class PopulationTask extends AsyncTask{
|
||||
$chunk->populateSkyLight();
|
||||
$chunk->setLightPopulated();
|
||||
$chunk->setPopulated();
|
||||
$this->chunk = $chunk->toFastBinary();
|
||||
$this->chunk = GenericChunk::fastSerialize($chunk);
|
||||
|
||||
$manager->setChunk($chunk->getX(), $chunk->getZ(), null);
|
||||
|
||||
@ -145,7 +141,7 @@ class PopulationTask extends AsyncTask{
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->{"chunk$i"} = $chunks[$i] !== null ? $chunks[$i]->toFastBinary() : null;
|
||||
$this->{"chunk$i"} = $chunks[$i] !== null ? GenericChunk::fastSerialize($chunks[$i]) : null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,10 +153,7 @@ class PopulationTask extends AsyncTask{
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var FullChunk $chunkC */
|
||||
$chunkC = $this->chunkClass;
|
||||
|
||||
$chunk = $chunkC::fromFastBinary($this->chunk, $level->getProvider());
|
||||
$chunk = GenericChunk::fastDeserialize($this->chunk, $level->getProvider());
|
||||
|
||||
if($chunk === null){
|
||||
//TODO error
|
||||
@ -173,7 +166,7 @@ class PopulationTask extends AsyncTask{
|
||||
}
|
||||
$c = $this->{"chunk$i"};
|
||||
if($c !== null){
|
||||
$c = $chunkC::fromFastBinary($c, $level->getProvider());
|
||||
$c = GenericChunk::fastDeserialize($c, $level->getProvider());
|
||||
$level->generateChunkCallback($c->getX(), $c->getZ(), $c);
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ use pocketmine\inventory\ChestInventory;
|
||||
use pocketmine\inventory\DoubleChestInventory;
|
||||
use pocketmine\inventory\InventoryHolder;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
@ -40,7 +40,7 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
/** @var DoubleChestInventory */
|
||||
protected $doubleInventory = null;
|
||||
|
||||
public function __construct(FullChunk $chunk, CompoundTag $nbt){
|
||||
public function __construct(Chunk $chunk, CompoundTag $nbt){
|
||||
parent::__construct($chunk, $nbt);
|
||||
$this->inventory = new ChestInventory($this);
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
namespace pocketmine\tile;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\nbt\tag\ShortTag;
|
||||
@ -30,7 +30,7 @@ use pocketmine\nbt\tag\StringTag;
|
||||
|
||||
class FlowerPot extends Spawnable{
|
||||
|
||||
public function __construct(FullChunk $chunk, CompoundTag $nbt){
|
||||
public function __construct(Chunk $chunk, CompoundTag $nbt){
|
||||
if(!isset($nbt->Item)){
|
||||
$nbt->item = new ShortTag("item", 0);
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ use pocketmine\inventory\FurnaceInventory;
|
||||
use pocketmine\inventory\FurnaceRecipe;
|
||||
use pocketmine\inventory\InventoryHolder;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
@ -41,7 +41,7 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
/** @var FurnaceInventory */
|
||||
protected $inventory;
|
||||
|
||||
public function __construct(FullChunk $chunk, CompoundTag $nbt){
|
||||
public function __construct(Chunk $chunk, CompoundTag $nbt){
|
||||
if(!isset($nbt->BurnTime) or $nbt["BurnTime"] < 0){
|
||||
$nbt->BurnTime = new ShortTag("BurnTime", 0);
|
||||
}
|
||||
|
@ -21,14 +21,14 @@
|
||||
|
||||
namespace pocketmine\tile;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
|
||||
class Sign extends Spawnable{
|
||||
|
||||
public function __construct(FullChunk $chunk, CompoundTag $nbt){
|
||||
public function __construct(Chunk $chunk, CompoundTag $nbt){
|
||||
if(!isset($nbt->Text1)){
|
||||
$nbt->Text1 = new StringTag("Text1", "");
|
||||
}
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
namespace pocketmine\tile;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
@ -34,7 +34,7 @@ class Skull extends Spawnable{
|
||||
const TYPE_HUMAN = 3;
|
||||
const TYPE_CREEPER = 4;
|
||||
|
||||
public function __construct(FullChunk $chunk, CompoundTag $nbt){
|
||||
public function __construct(Chunk $chunk, CompoundTag $nbt){
|
||||
if(!isset($nbt->SkullType)){
|
||||
$nbt->SkullType = new ByteTag("SkullType", 0);
|
||||
}
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
namespace pocketmine\tile;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\network\protocol\BlockEntityDataPacket;
|
||||
@ -51,7 +51,7 @@ abstract class Spawnable extends Tile{
|
||||
*/
|
||||
public abstract function getSpawnCompound();
|
||||
|
||||
public function __construct(FullChunk $chunk, CompoundTag $nbt){
|
||||
public function __construct(Chunk $chunk, CompoundTag $nbt){
|
||||
parent::__construct($chunk, $nbt);
|
||||
$this->spawnToAll();
|
||||
}
|
||||
|
@ -26,7 +26,6 @@ namespace pocketmine\tile;
|
||||
|
||||
use pocketmine\event\Timings;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\level\Position;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
@ -68,13 +67,13 @@ abstract class Tile extends Position{
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param FullChunk $chunk
|
||||
* @param Chunk $chunk
|
||||
* @param CompoundTag $nbt
|
||||
* @param $args
|
||||
*
|
||||
* @return Tile
|
||||
*/
|
||||
public static function createTile($type, FullChunk $chunk, CompoundTag $nbt, ...$args){
|
||||
public static function createTile($type, Chunk $chunk, CompoundTag $nbt, ...$args){
|
||||
if(isset(self::$knownTiles[$type])){
|
||||
$class = self::$knownTiles[$type];
|
||||
return new $class($chunk, $nbt, ...$args);
|
||||
@ -108,7 +107,7 @@ abstract class Tile extends Position{
|
||||
return self::$shortNames[static::class];
|
||||
}
|
||||
|
||||
public function __construct(FullChunk $chunk, CompoundTag $nbt){
|
||||
public function __construct(Chunk $chunk, CompoundTag $nbt){
|
||||
assert($chunk !== null and $chunk->getProvider() !== null);
|
||||
|
||||
$this->timings = Timings::getTileEntityTimings($this);
|
||||
@ -163,7 +162,7 @@ abstract class Tile extends Position{
|
||||
if(!$this->closed){
|
||||
$this->closed = true;
|
||||
unset($this->level->updateTiles[$this->id]);
|
||||
if($this->chunk instanceof FullChunk){
|
||||
if($this->chunk instanceof Chunk){
|
||||
$this->chunk->removeTile($this);
|
||||
}
|
||||
if(($level = $this->getLevel()) instanceof Level){
|
||||
|
Loading…
x
Reference in New Issue
Block a user