Added support for extra data, improved BinaryStream

This commit is contained in:
Shoghi Cervantes 2015-08-04 18:29:13 +02:00
parent 7fd053fb09
commit 9456e20770
11 changed files with 271 additions and 40 deletions

View File

@ -90,6 +90,7 @@ use pocketmine\nbt\tag\String;
use pocketmine\network\Network;
use pocketmine\network\protocol\DataPacket;
use pocketmine\network\protocol\FullChunkDataPacket;
use pocketmine\network\protocol\LevelEventPacket;
use pocketmine\network\protocol\MoveEntityPacket;
use pocketmine\network\protocol\SetEntityMotionPacket;
use pocketmine\network\protocol\SetTimePacket;
@ -280,6 +281,10 @@ class Level implements ChunkManager, Metadatable{
return PHP_INT_SIZE === 8 ? (($x & 0xFFFFFFF) << 35) | (($y & 0x7f) << 28) | ($z & 0xFFFFFFF) : $x . ":" . $y .":". $z;
}
public static function chunkBlockHash($x, $y, $z){
return ($x << 11) | ($z << 7) | $y;
}
public static function getBlockXYZ($hash, &$x, &$y, &$z){
if(PHP_INT_SIZE === 8){
$x = ($hash >> 35) << 36 >> 36;
@ -798,6 +803,17 @@ class Level implements ChunkManager, Metadatable{
}
}
public function sendBlockExtraData($x, $y, $z, $id, $data, array $targets = null){
$pk = new LevelEventPacket;
$pk->evid = LevelEventPacket::EVENT_SET_DATA;
$pk->x = $x + 0.5;
$pk->y = $y + 0.5;
$pk->z = $z + 0.5;
$pk->data = ($data << 8) | $id;
Server::broadcastPacket($targets === null ? $this->getChunkPlayers($x >> 4, $z >> 4) : $targets, $pk->setChannel(Network::CHANNEL_WORLD_EVENTS));
}
/**
* @param Player[] $target
* @param Block[] $blocks
@ -1920,6 +1936,34 @@ class Level implements ChunkManager, Metadatable{
}
}
/**
* Gets the raw block extra data
*
* @param int $x
* @param int $y
* @param int $z
*
* @return int 16-bit
*/
public function getBlockExtraDataAt($x, $y, $z){
return $this->getChunk($x >> 4, $z >> 4, true)->getBlockExtraData($x & 0x0f, $y & 0x7f, $z & 0x0f);
}
/**
* Sets the raw block metadata.
*
* @param int $x
* @param int $y
* @param int $z
* @param int $id
* @param int $data
*/
public function setBlockExtraDataAt($x, $y, $z, $id, $data){
$this->getChunk($x >> 4, $z >> 4, true)->setBlockExtraData($x & 0x0f, $y & 0x7f, $z & 0x0f, ($data << 8) | $id);
$this->sendBlockExtraData($x, $y, $z, $id, $data);
}
/**
* Gets the raw block metadata
*

View File

@ -119,6 +119,23 @@ interface FullChunk{
*/
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
@ -312,6 +329,8 @@ interface FullChunk{
public function getBlockDataArray();
public function getBlockExtraDataArray();
public function getBlockSkyLightArray();
public function getBlockLightArray();

View File

@ -30,6 +30,7 @@ use pocketmine\nbt\tag\ByteArray;
use pocketmine\nbt\tag\Compound;
use pocketmine\network\protocol\FullChunkDataPacket;
use pocketmine\tile\Spawnable;
use pocketmine\utils\BinaryStream;
use pocketmine\utils\ChunkException;
use raklib\Binary;
@ -89,14 +90,20 @@ class Anvil extends McRegion{
$tiles = $nbt->write();
}
$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()) .
//TODO extra data
Binary::writeInt(0) .
$extraData->getBuffer() .
$tiles;
$this->getLevel()->chunkRequestCallback($x, $z, $ordered, FullChunkDataPacket::ORDER_LAYERED);

View File

@ -34,6 +34,7 @@ use pocketmine\nbt\tag\IntArray;
use pocketmine\nbt\tag\Long;
use pocketmine\Player;
use pocketmine\utils\Binary;
use pocketmine\utils\BinaryStream;
class Chunk extends BaseChunk{
@ -92,14 +93,27 @@ class Chunk extends BaseChunk{
}
}
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->ExtraData) or !($this->nbt->ExtraData instanceof ByteArray)){
$this->nbt->ExtraData = new ByteArray("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);
unset($this->nbt->Sections, $this->nbt->ExtraData);
}
public function isLightPopulated(){
@ -240,6 +254,16 @@ class Chunk extends BaseChunk{
$nbt->TileEntities = new Enum("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 ByteArray("ExtraData", $extraData->getBuffer());
$writer = new NBT(NBT::BIG_ENDIAN);
$nbt->setName("Level");
$writer->setData(new Compound("", ["Level" => $nbt]));
@ -293,6 +317,16 @@ class Chunk extends BaseChunk{
$nbt->TileEntities = new Enum("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 ByteArray("ExtraData", $extraData->getBuffer());
$writer = new NBT(NBT::BIG_ENDIAN);
$nbt->setName("Level");
$writer->setData(new Compound("", ["Level" => $nbt]));

View File

@ -26,6 +26,7 @@ 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\Compound;
use pocketmine\Player;
use pocketmine\tile\Tile;
@ -59,6 +60,8 @@ abstract class BaseFullChunk implements FullChunk{
protected $NBTentities;
protected $extraData = [];
/** @var LevelProvider */
protected $provider;
@ -82,7 +85,7 @@ abstract class BaseFullChunk implements FullChunk{
* @param Compound[] $entities
* @param Compound[] $tiles
*/
protected function __construct($provider, $x, $z, $blocks, $data, $skyLight, $blockLight, array $biomeColors = [], array $heightMap = [], array $entities = [], array $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;
@ -95,7 +98,7 @@ abstract class BaseFullChunk implements FullChunk{
if(count($biomeColors) === 256){
$this->biomeColors = $biomeColors;
}else{
$this->biomeColors = array_fill(0, 256, Binary::readInt("\xff\x00\x00\x00\x00"));
$this->biomeColors = array_fill(0, 256, 0);
}
if(count($heightMap) === 256){
@ -104,6 +107,8 @@ abstract class BaseFullChunk implements FullChunk{
$this->heightMap = array_fill(0, 256, 127);
}
$this->extraData = $extraData;
$this->NBTtiles = $tiles;
$this->NBTentities = $entities;
}
@ -254,6 +259,24 @@ abstract class BaseFullChunk implements FullChunk{
}
}
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){
@ -336,6 +359,10 @@ abstract class BaseFullChunk implements FullChunk{
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;

View File

@ -26,6 +26,7 @@ use pocketmine\level\format\LevelProvider;
use pocketmine\nbt\NBT;
use pocketmine\Player;
use pocketmine\utils\Binary;
use pocketmine\utils\BinaryStream;
class Chunk extends BaseFullChunk{
@ -260,6 +261,7 @@ class Chunk extends BaseFullChunk{
$entities = null;
$tiles = null;
$extraData = [];
if($provider instanceof LevelDB){
$nbt = new NBT(NBT::LITTLE_ENDIAN);
@ -280,6 +282,16 @@ class Chunk extends BaseFullChunk{
$tiles = [$tiles];
}
}
$tileData = $provider->getDatabase()->get(substr($data, 0, 8) . LevelDB::ENTRY_EXTRA_DATA);
if($tileData !== false and strlen($tileData) > 0){
$stream = new BinaryStream($tileData);
$count = $stream->getInt();
for($i = 0; $i < $count; ++$i){
$key = $stream->getInt();
$value = $stream->getShort(false);
$extraData[$key] = $value;
}
}
}
$chunk = new Chunk($provider instanceof LevelProvider ? $provider : LevelDB::class, $chunkX, $chunkZ, $chunkData, $entities, $tiles);
@ -341,6 +353,17 @@ class Chunk extends BaseFullChunk{
$provider->getDatabase()->delete($chunkIndex . LevelDB::ENTRY_TILES);
}
if(count($this->getBlockExtraDataArray()) > 0){
$extraData = new BinaryStream();
$extraData->putInt(count($this->getBlockExtraDataArray()));
foreach($this->getBlockExtraDataArray() as $key => $value){
$extraData->putInt($key);
$extraData->putShort($value);
}
$provider->getDatabase()->put($chunkIndex . LevelDB::ENTRY_EXTRA_DATA, $extraData->getBuffer());
}else{
$provider->getDatabase()->delete($chunkIndex . LevelDB::ENTRY_EXTRA_DATA);
}
}

View File

@ -33,6 +33,7 @@ use pocketmine\nbt\tag\Long;
use pocketmine\nbt\tag\String;
use pocketmine\tile\Spawnable;
use pocketmine\utils\Binary;
use pocketmine\utils\BinaryStream;
use pocketmine\utils\ChunkException;
use pocketmine\utils\LevelException;
@ -40,6 +41,7 @@ class LevelDB extends BaseLevelProvider{
const ENTRY_VERSION = "v";
const ENTRY_FLAGS = "f";
const ENTRY_EXTRA_DATA = "4";
const ENTRY_TICKS = "3";
const ENTRY_ENTITIES = "2";
const ENTRY_TILES = "1";
@ -156,14 +158,20 @@ class LevelDB extends BaseLevelProvider{
$heightmap = pack("C*", ...$chunk->getHeightMapArray());
$biomeColors = pack("N*", ...$chunk->getBiomeColorArray());
$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() .
$heightmap .
$biomeColors .
//TODO extra data
Binary::writeInt(0) .
$extraData->getBuffer() .
$tiles;
$this->getLevel()->chunkRequestCallback($x, $z, $ordered);

View File

@ -33,6 +33,7 @@ use pocketmine\nbt\tag\IntArray;
use pocketmine\nbt\tag\Long;
use pocketmine\Player;
use pocketmine\utils\Binary;
use pocketmine\utils\BinaryStream;
class Chunk extends BaseFullChunk{
@ -87,7 +88,20 @@ class Chunk extends BaseFullChunk{
$this->nbt->BlockLight = new ByteArray("BlockLight", $half);
}
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->ExtraData) or !($this->nbt->ExtraData instanceof ByteArray)){
$this->nbt->ExtraData = new ByteArray("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());
@ -395,6 +409,16 @@ class Chunk extends BaseFullChunk{
$nbt->TileEntities = new Enum("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 ByteArray("ExtraData", $extraData->getBuffer());
$writer = new NBT(NBT::BIG_ENDIAN);
$nbt->setName("Level");
$writer->setData(new Compound("", ["Level" => $nbt]));

View File

@ -33,6 +33,7 @@ use pocketmine\nbt\tag\Long;
use pocketmine\nbt\tag\String;
use pocketmine\tile\Spawnable;
use pocketmine\utils\Binary;
use pocketmine\utils\BinaryStream;
use pocketmine\utils\ChunkException;
class McRegion extends BaseLevelProvider{
@ -132,14 +133,20 @@ class McRegion extends BaseLevelProvider{
$tiles = $nbt->write();
}
$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()) .
//TODO extra data
Binary::writeInt(0) .
$extraData->getBuffer() .
$tiles;
$this->getLevel()->chunkRequestCallback($x, $z, $ordered);

View File

@ -48,7 +48,7 @@ abstract class DataPacket extends BinaryStream{
abstract public function decode();
protected function reset(){
public function reset(){
$this->buffer = chr($this::NETWORK_ID);
$this->offset = 0;
}

View File

@ -33,10 +33,15 @@ use pocketmine\utils\UUID;
class BinaryStream extends \stdClass{
public $offset = 0;
public $buffer = "";
public $offset;
public $buffer;
public function __construct($buffer = "", $offset = 0){
$this->buffer = $buffer;
$this->offset = $offset;
}
protected function reset(){
public function reset(){
$this->buffer = "";
$this->offset = 0;
}
@ -54,7 +59,7 @@ class BinaryStream extends \stdClass{
return $this->buffer;
}
protected function get($len){
public function get($len){
if($len < 0){
$this->offset = strlen($this->buffer) - 1;
return "";
@ -65,68 +70,101 @@ class BinaryStream extends \stdClass{
return $len === 1 ? $this->buffer{$this->offset++} : substr($this->buffer, ($this->offset += $len) - $len, $len);
}
protected function put($str){
public function put($str){
$this->buffer .= $str;
}
protected function getLong(){
public function getLong(){
return Binary::readLong($this->get(8));
}
protected function putLong($v){
public function putLong($v){
$this->buffer .= Binary::writeLong($v);
}
protected function getInt(){
public function getInt(){
return Binary::readInt($this->get(4));
}
protected function putInt($v){
public function putInt($v){
$this->buffer .= Binary::writeInt($v);
}
protected function getShort($signed = true){
public function getLLong(){
return Binary::readLLong($this->get(8));
}
public function putLLong($v){
$this->buffer .= Binary::writeLLong($v);
}
public function getLInt(){
return Binary::readLInt($this->get(4));
}
public function putLInt($v){
$this->buffer .= Binary::writeLInt($v);
}
public function getShort($signed = true){
return $signed ? Binary::readSignedShort($this->get(2)) : Binary::readShort($this->get(2));
}
protected function putShort($v){
public function putShort($v){
$this->buffer .= Binary::writeShort($v);
}
protected function getFloat(){
public function getFloat(){
return Binary::readFloat($this->get(4));
}
protected function putFloat($v){
public function putFloat($v){
$this->buffer .= Binary::writeFloat($v);
}
protected function getTriad(){
public function getLShort($signed = true){
return $signed ? Binary::readSignedLShort($this->get(2)) : Binary::readLShort($this->get(2));
}
public function putLShort($v){
$this->buffer .= Binary::writeLShort($v);
}
public function getLFloat(){
return Binary::readLFloat($this->get(4));
}
public function putLFloat($v){
$this->buffer .= Binary::writeLFloat($v);
}
public function getTriad(){
return Binary::readTriad($this->get(3));
}
protected function putTriad($v){
public function putTriad($v){
$this->buffer .= Binary::writeTriad($v);
}
protected function getLTriad(){
public function getLTriad(){
return Binary::readLTriad($this->get(3));
}
protected function putLTriad($v){
public function putLTriad($v){
$this->buffer .= Binary::writeLTriad($v);
}
protected function getByte(){
public function getByte(){
return ord($this->buffer{$this->offset++});
}
protected function putByte($v){
public function putByte($v){
$this->buffer .= chr($v);
}
protected function getDataArray($len = 10){
public function getDataArray($len = 10){
$data = [];
for($i = 1; $i <= $len and !$this->feof(); ++$i){
$data[] = $this->get($this->getTriad());
@ -135,22 +173,22 @@ class BinaryStream extends \stdClass{
return $data;
}
protected function putDataArray(array $data = []){
public function putDataArray(array $data = []){
foreach($data as $v){
$this->putTriad(strlen($v));
$this->put($v);
}
}
protected function getUUID(){
public function getUUID(){
return UUID::fromBinary($this->get(16));
}
protected function putUUID(UUID $uuid){
public function putUUID(UUID $uuid){
$this->put($uuid->toBinary());
}
protected function getSlot(){
public function getSlot(){
$id = $this->getShort(true);
if($id <= 0){
@ -177,7 +215,7 @@ class BinaryStream extends \stdClass{
);
}
protected function putSlot(Item $item){
public function putSlot(Item $item){
if($item->getId() === 0){
$this->putShort(0);
return;
@ -192,16 +230,16 @@ class BinaryStream extends \stdClass{
}
protected function getString(){
public function getString(){
return $this->get($this->getShort());
}
protected function putString($v){
public function putString($v){
$this->putShort(strlen($v));
$this->put($v);
}
protected function feof(){
public function feof(){
return !isset($this->buffer{$this->offset});
}
}