Merge PR #1775: NBT streams refactor

This commit is contained in:
Dylan K. Taylor 2017-12-30 15:53:47 +00:00 committed by GitHub
commit 3ea72a0bf9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 606 additions and 344 deletions

View File

@ -91,7 +91,7 @@ use pocketmine\level\WeakPosition;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Vector3;
use pocketmine\metadata\MetadataValue;
use pocketmine\nbt\NBT;
use pocketmine\nbt\NetworkLittleEndianNBTStream;
use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\DoubleTag;
@ -2834,8 +2834,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$t = $this->level->getTile($pos);
if($t instanceof Spawnable){
$nbt = new NBT(NBT::LITTLE_ENDIAN);
$nbt->read($packet->namedtag, false, true);
$nbt = new NetworkLittleEndianNBTStream();
$nbt->read($packet->namedtag);
$nbt = $nbt->getData();
if(!$t->updateCompoundTag($nbt, $this)){
$t->spawnTo($this);

View File

@ -67,6 +67,7 @@ use pocketmine\level\LevelException;
use pocketmine\metadata\EntityMetadataStore;
use pocketmine\metadata\LevelMetadataStore;
use pocketmine\metadata\PlayerMetadataStore;
use pocketmine\nbt\BigEndianNBTStream;
use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\CompoundTag;
@ -749,7 +750,7 @@ class Server{
if($this->shouldSavePlayerData()){
if(file_exists($path . "$name.dat")){
try{
$nbt = new NBT(NBT::BIG_ENDIAN);
$nbt = new BigEndianNBTStream();
$nbt->readCompressed(file_get_contents($path . "$name.dat"));
return $nbt->getData();
@ -815,7 +816,7 @@ class Server{
$this->pluginManager->callEvent($ev);
if(!$ev->isCancelled()){
$nbt = new NBT(NBT::BIG_ENDIAN);
$nbt = new BigEndianNBTStream();
try{
$nbt->setData($ev->getSaveData());

View File

@ -34,6 +34,7 @@ use pocketmine\item\enchantment\Enchantment;
use pocketmine\item\enchantment\EnchantmentInstance;
use pocketmine\level\Level;
use pocketmine\math\Vector3;
use pocketmine\nbt\LittleEndianNBTStream;
use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\CompoundTag;
@ -64,7 +65,7 @@ class Item implements ItemIds, \JsonSerializable{
}
if(self::$cachedParser === null){
self::$cachedParser = new NBT(NBT::LITTLE_ENDIAN);
self::$cachedParser = new LittleEndianNBTStream();
}
self::$cachedParser->read($tag);
@ -79,7 +80,7 @@ class Item implements ItemIds, \JsonSerializable{
private static function writeCompoundTag(CompoundTag $tag) : string{
if(self::$cachedParser === null){
self::$cachedParser = new NBT(NBT::LITTLE_ENDIAN);
self::$cachedParser = new LittleEndianNBTStream();
}
self::$cachedParser->setData($tag);

View File

@ -29,7 +29,7 @@ use pocketmine\level\generator\Generator;
use pocketmine\level\Level;
use pocketmine\level\LevelException;
use pocketmine\math\Vector3;
use pocketmine\nbt\NBT;
use pocketmine\nbt\BigEndianNBTStream;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\scheduler\AsyncTask;
@ -48,7 +48,7 @@ abstract class BaseLevelProvider implements LevelProvider{
if(!file_exists($this->path)){
mkdir($this->path, 0777, true);
}
$nbt = new NBT(NBT::BIG_ENDIAN);
$nbt = new BigEndianNBTStream();
$nbt->readCompressed(file_get_contents($this->getPath() . "level.dat"));
$levelData = $nbt->getData()->getCompoundTag("Data");
if($levelData !== null){
@ -120,7 +120,7 @@ abstract class BaseLevelProvider implements LevelProvider{
}
public function saveLevelData(){
$nbt = new NBT(NBT::BIG_ENDIAN);
$nbt = new BigEndianNBTStream();
$nbt->setData(new CompoundTag("", [
$this->levelData
]));

View File

@ -32,7 +32,7 @@ use pocketmine\level\generator\Flat;
use pocketmine\level\generator\Generator;
use pocketmine\level\Level;
use pocketmine\level\LevelException;
use pocketmine\nbt\NBT;
use pocketmine\nbt\LittleEndianNBTStream;
use pocketmine\nbt\tag\{
ByteTag, CompoundTag, FloatTag, IntTag, LongTag, StringTag
};
@ -94,7 +94,7 @@ class LevelDB extends BaseLevelProvider{
if(!file_exists($this->path)){
mkdir($this->path, 0777, true);
}
$nbt = new NBT(NBT::LITTLE_ENDIAN);
$nbt = new LittleEndianNBTStream();
$nbt->read(substr(file_get_contents($this->getPath() . "level.dat"), 8));
$levelData = $nbt->getData();
if($levelData instanceof CompoundTag){
@ -217,7 +217,7 @@ class LevelDB extends BaseLevelProvider{
new StringTag("generatorOptions", $options["preset"] ?? "")
]);
$nbt = new NBT(NBT::LITTLE_ENDIAN);
$nbt = new LittleEndianNBTStream();
$nbt->setData($levelData);
$buffer = $nbt->write();
file_put_contents($path . "level.dat", Binary::writeLInt(self::CURRENT_STORAGE_VERSION) . Binary::writeLInt(strlen($buffer)) . $buffer);
@ -247,7 +247,7 @@ class LevelDB extends BaseLevelProvider{
$this->levelData->setInt("NetworkVersion", ProtocolInfo::CURRENT_PROTOCOL);
$this->levelData->setInt("StorageVersion", self::CURRENT_STORAGE_VERSION);
$nbt = new NBT(NBT::LITTLE_ENDIAN);
$nbt = new LittleEndianNBTStream();
$nbt->setData($this->levelData);
$buffer = $nbt->write();
file_put_contents($this->getPath() . "level.dat", Binary::writeLInt(self::CURRENT_STORAGE_VERSION) . Binary::writeLInt(strlen($buffer)) . $buffer);
@ -416,7 +416,7 @@ class LevelDB extends BaseLevelProvider{
throw new UnsupportedChunkFormatException("don't know how to decode chunk format version $chunkVersion");
}
$nbt = new NBT(NBT::LITTLE_ENDIAN);
$nbt = new LittleEndianNBTStream();
$entities = [];
if(($entityData = $this->db->get($index . self::TAG_ENTITY)) !== false and strlen($entityData) > 0){
@ -553,7 +553,7 @@ class LevelDB extends BaseLevelProvider{
*/
private function writeTags(array $targets, string $index){
if(!empty($targets)){
$nbt = new NBT(NBT::LITTLE_ENDIAN);
$nbt = new LittleEndianNBTStream();
$nbt->setData($targets);
$this->db->put($index, $nbt->write());
}else{

View File

@ -27,6 +27,7 @@ use pocketmine\level\format\Chunk;
use pocketmine\level\format\ChunkException;
use pocketmine\level\format\io\ChunkUtils;
use pocketmine\level\format\SubChunk;
use pocketmine\nbt\BigEndianNBTStream;
use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\ByteArrayTag;
use pocketmine\nbt\tag\CompoundTag;
@ -83,7 +84,7 @@ class Anvil extends McRegion{
//TODO: TileTicks
$writer = new NBT(NBT::BIG_ENDIAN);
$writer = new BigEndianNBTStream();
$nbt->setName("Level");
$writer->setData(new CompoundTag("", [$nbt]));
@ -100,7 +101,7 @@ class Anvil extends McRegion{
}
public function nbtDeserialize(string $data){
$nbt = new NBT(NBT::BIG_ENDIAN);
$nbt = new BigEndianNBTStream();
try{
$nbt->readCompressed($data);

View File

@ -30,6 +30,7 @@ use pocketmine\level\format\io\ChunkUtils;
use pocketmine\level\format\SubChunk;
use pocketmine\level\generator\Generator;
use pocketmine\level\Level;
use pocketmine\nbt\BigEndianNBTStream;
use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\{
ByteArrayTag, ByteTag, CompoundTag, IntArrayTag, IntTag, ListTag, LongTag, StringTag
@ -102,7 +103,7 @@ class McRegion extends BaseLevelProvider{
$nbt->setTag(new ListTag("TileEntities", $tiles, NBT::TAG_Compound));
$writer = new NBT(NBT::BIG_ENDIAN);
$writer = new BigEndianNBTStream();
$nbt->setName("Level");
$writer->setData(new CompoundTag("", [$nbt]));
@ -115,7 +116,7 @@ class McRegion extends BaseLevelProvider{
* @return Chunk|null
*/
public function nbtDeserialize(string $data){
$nbt = new NBT(NBT::BIG_ENDIAN);
$nbt = new BigEndianNBTStream();
try{
$nbt->readCompressed($data);
@ -258,7 +259,7 @@ class McRegion extends BaseLevelProvider{
new StringTag("LevelName", $name),
new CompoundTag("GameRules", [])
]);
$nbt = new NBT(NBT::BIG_ENDIAN);
$nbt = new BigEndianNBTStream();
$nbt->setData(new CompoundTag("", [
$levelData
]));

View File

@ -0,0 +1,87 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\nbt;
#ifndef COMPILE
use pocketmine\utils\Binary;
#endif
#include <rules/NBT.h>
class BigEndianNBTStream extends NBTStream{
public function getShort() : int{
return Binary::readShort($this->get(2));
}
public function getSignedShort() : int{
return Binary::readSignedShort($this->get(2));
}
public function putShort(int $v) : void{
$this->buffer .= Binary::writeShort($v);
}
public function getInt() : int{
return Binary::readInt($this->get(4));
}
public function putInt(int $v) : void{
$this->buffer .= Binary::writeInt($v);
}
public function getLong() : int{
return Binary::readLong($this->get(8));
}
public function putLong(int $v) : void{
$this->buffer .= Binary::writeLong($v);
}
public function getFloat() : float{
return Binary::readFloat($this->get(4));
}
public function putFloat(float $v) : void{
$this->buffer .= Binary::writeFloat($v);
}
public function getDouble() : float{
return Binary::readDouble($this->get(8));
}
public function putDouble(float $v) : void{
$this->buffer .= Binary::writeDouble($v);
}
public function getIntArray() : array{
$len = $this->getInt();
return array_values(unpack("N*", $this->get($len * 4)));
}
public function putIntArray(array $array) : void{
$this->putInt(count($array));
$this->put(pack("N*", ...$array));
}
}

View File

@ -0,0 +1,87 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\nbt;
#ifndef COMPILE
use pocketmine\utils\Binary;
#endif
#include <rules/NBT.h>
class LittleEndianNBTStream extends NBTStream{
public function getShort() : int{
return Binary::readLShort($this->get(2));
}
public function getSignedShort() : int{
return Binary::readSignedLShort($this->get(2));
}
public function putShort(int $v) : void{
$this->put(Binary::writeLShort($v));
}
public function getInt() : int{
return Binary::readLInt($this->get(4));
}
public function putInt(int $v) : void{
$this->put(Binary::writeLInt($v));
}
public function getLong() : int{
return Binary::readLLong($this->get(8));
}
public function putLong(int $v) : void{
$this->put(Binary::writeLLong($v));
}
public function getFloat() : float{
return Binary::readLFloat($this->get(4));
}
public function putFloat(float $v) : void{
$this->put(Binary::writeLFloat($v));
}
public function getDouble() : float{
return Binary::readLDouble($this->get(8));
}
public function putDouble(float $v) : void{
$this->put(Binary::writeLDouble($v));
}
public function getIntArray() : array{
$len = $this->getInt();
return array_values(unpack("V*", $this->get($len * 4)));
}
public function putIntArray(array $array) : void{
$this->putInt(count($array));
$this->put(pack("V*", ...$array));
}
}

View File

@ -36,22 +36,11 @@ use pocketmine\nbt\tag\IntArrayTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\LongTag;
use pocketmine\nbt\tag\NamedTag;
use pocketmine\nbt\tag\ShortTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\nbt\tag\Tag;
#ifndef COMPILE
use pocketmine\utils\Binary;
#endif
#include <rules/NBT.h>
/**
* Named Binary Tag encoder/decoder
*/
class NBT{
abstract class NBT{
public const LITTLE_ENDIAN = 0;
public const BIG_ENDIAN = 1;
@ -68,17 +57,12 @@ class NBT{
public const TAG_Compound = 10;
public const TAG_IntArray = 11;
public $buffer;
public $offset;
public $endianness;
private $data;
/**
* @param int $type
*
* @return Tag
*/
public static function createTag(int $type){
public static function createTag(int $type) : Tag{
switch($type){
case self::TAG_End:
return new EndTag();
@ -172,260 +156,4 @@ class NBT{
return true;
}
public function get($len){
if($len < 0){
$this->offset = strlen($this->buffer) - 1;
return "";
}elseif($len === true){
return substr($this->buffer, $this->offset);
}
return $len === 1 ? $this->buffer{$this->offset++} : substr($this->buffer, ($this->offset += $len) - $len, $len);
}
public function put($v){
$this->buffer .= $v;
}
public function feof() : bool{
return !isset($this->buffer{$this->offset});
}
public function __construct($endianness = self::LITTLE_ENDIAN){
$this->offset = 0;
$this->endianness = $endianness & 0x01;
}
public function read($buffer, $doMultiple = false, bool $network = false){
$this->offset = 0;
$this->buffer = $buffer;
$this->data = $this->readTag($network);
if($doMultiple and $this->offset < strlen($this->buffer)){
$this->data = [$this->data];
do{
$this->data[] = $this->readTag($network);
}while($this->offset < strlen($this->buffer));
}
$this->buffer = "";
}
public function readCompressed($buffer){
$this->read(zlib_decode($buffer));
}
/**
* @param bool $network
*
* @return string|bool
*/
public function write(bool $network = false){
$this->offset = 0;
$this->buffer = "";
if($this->data instanceof CompoundTag){
$this->writeTag($this->data, $network);
return $this->buffer;
}elseif(is_array($this->data)){
foreach($this->data as $tag){
$this->writeTag($tag, $network);
}
return $this->buffer;
}
return false;
}
public function writeCompressed($compression = ZLIB_ENCODING_GZIP, $level = 7){
if(($write = $this->write()) !== false){
return zlib_encode($write, $compression, $level);
}
return false;
}
public function readTag(bool $network = false){
if($this->feof()){
return new EndTag();
}
$tagType = $this->getByte();
$tag = self::createTag($tagType);
if($tag instanceof NamedTag){
$tag->setName($this->getString($network));
$tag->read($this, $network);
}
return $tag;
}
public function writeTag(Tag $tag, bool $network = false){
$this->putByte($tag->getType());
if($tag instanceof NamedTag){
$this->putString($tag->getName(), $network);
}
$tag->write($this, $network);
}
public function getByte() : int{
return Binary::readByte($this->get(1));
}
public function getSignedByte() : int{
return Binary::readSignedByte($this->get(1));
}
public function putByte($v){
$this->buffer .= Binary::writeByte($v);
}
public function getShort() : int{
return $this->endianness === self::BIG_ENDIAN ? Binary::readShort($this->get(2)) : Binary::readLShort($this->get(2));
}
public function getSignedShort() : int{
return $this->endianness === self::BIG_ENDIAN ? Binary::readSignedShort($this->get(2)) : Binary::readSignedLShort($this->get(2));
}
public function putShort($v){
$this->buffer .= $this->endianness === self::BIG_ENDIAN ? Binary::writeShort($v) : Binary::writeLShort($v);
}
public function getInt(bool $network = false) : int{
if($network === true){
return Binary::readVarInt($this->buffer, $this->offset);
}
return $this->endianness === self::BIG_ENDIAN ? Binary::readInt($this->get(4)) : Binary::readLInt($this->get(4));
}
public function putInt($v, bool $network = false){
if($network === true){
$this->buffer .= Binary::writeVarInt($v);
}else{
$this->buffer .= $this->endianness === self::BIG_ENDIAN ? Binary::writeInt($v) : Binary::writeLInt($v);
}
}
public function getLong(bool $network = false) : int{
if($network){
return Binary::readVarLong($this->buffer, $this->offset);
}
return $this->endianness === self::BIG_ENDIAN ? Binary::readLong($this->get(8)) : Binary::readLLong($this->get(8));
}
public function putLong($v, bool $network = false){
if($network){
$this->buffer .= Binary::writeVarLong($v);
}else{
$this->buffer .= $this->endianness === self::BIG_ENDIAN ? Binary::writeLong($v) : Binary::writeLLong($v);
}
}
public function getFloat() : float{
return $this->endianness === self::BIG_ENDIAN ? Binary::readFloat($this->get(4)) : Binary::readLFloat($this->get(4));
}
public function putFloat($v){
$this->buffer .= $this->endianness === self::BIG_ENDIAN ? Binary::writeFloat($v) : Binary::writeLFloat($v);
}
public function getDouble() : float{
return $this->endianness === self::BIG_ENDIAN ? Binary::readDouble($this->get(8)) : Binary::readLDouble($this->get(8));
}
public function putDouble($v){
$this->buffer .= $this->endianness === self::BIG_ENDIAN ? Binary::writeDouble($v) : Binary::writeLDouble($v);
}
public function getString(bool $network = false){
$len = $network ? Binary::readUnsignedVarInt($this->buffer, $this->offset) : $this->getShort();
return $this->get($len);
}
public function putString($v, bool $network = false){
if($network === true){
$this->put(Binary::writeUnsignedVarInt(strlen($v)));
}else{
$this->putShort(strlen($v));
}
$this->buffer .= $v;
}
public function getArray() : array{
$data = [];
self::toArray($data, $this->data);
return $data;
}
private static function toArray(array &$data, Tag $tag){
/** @var CompoundTag[]|ListTag[]|IntArrayTag[] $tag */
foreach($tag as $key => $value){
if($value instanceof CompoundTag or $value instanceof ListTag or $value instanceof IntArrayTag){
$data[$key] = [];
self::toArray($data[$key], $value);
}else{
$data[$key] = $value->getValue();
}
}
}
public static function fromArrayGuesser($key, $value){
if(is_int($value)){
return new IntTag($key, $value);
}elseif(is_float($value)){
return new FloatTag($key, $value);
}elseif(is_string($value)){
return new StringTag($key, $value);
}elseif(is_bool($value)){
return new ByteTag($key, $value ? 1 : 0);
}
return null;
}
private static function fromArray(Tag $tag, array $data, callable $guesser){
foreach($data as $key => $value){
if(is_array($value)){
$isNumeric = true;
$isIntArray = true;
foreach($value as $k => $v){
if(!is_numeric($k)){
$isNumeric = false;
break;
}elseif(!is_int($v)){
$isIntArray = false;
}
}
$tag{$key} = $isNumeric ? ($isIntArray ? new IntArrayTag($key, []) : new ListTag($key, [])) : new CompoundTag($key, []);
self::fromArray($tag->{$key}, $value, $guesser);
}else{
$v = call_user_func($guesser, $key, $value);
if($v instanceof Tag){
$tag{$key} = $v;
}
}
}
}
public function setArray(array $data, callable $guesser = null){
$this->data = new CompoundTag("", []);
self::fromArray($this->data, $data, $guesser ?? [self::class, "fromArrayGuesser"]);
}
/**
* @return CompoundTag|array
*/
public function getData(){
return $this->data;
}
/**
* @param CompoundTag|array $data
*/
public function setData($data){
$this->data = $data;
}
}

View File

@ -0,0 +1,271 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\nbt;
use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\EndTag;
use pocketmine\nbt\tag\FloatTag;
use pocketmine\nbt\tag\IntArrayTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\NamedTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\nbt\tag\Tag;
#ifndef COMPILE
use pocketmine\utils\Binary;
#endif
#include <rules/NBT.h>
/**
* Base Named Binary Tag encoder/decoder
*/
abstract class NBTStream{
public $buffer;
public $offset;
private $data;
public function get($len) : string{
if($len < 0){
$this->offset = strlen($this->buffer) - 1;
return "";
}elseif($len === true){
return substr($this->buffer, $this->offset);
}
return $len === 1 ? $this->buffer{$this->offset++} : substr($this->buffer, ($this->offset += $len) - $len, $len);
}
public function put(string $v) : void{
$this->buffer .= $v;
}
public function feof() : bool{
return !isset($this->buffer{$this->offset});
}
public function __construct(){
$this->offset = 0;
$this->buffer = "";
}
public function read(string $buffer, bool $doMultiple = false) : void{
$this->offset = 0;
$this->buffer = $buffer;
$this->data = $this->readTag();
if($doMultiple and $this->offset < strlen($this->buffer)){
$this->data = [$this->data];
do{
$this->data[] = $this->readTag();
}while($this->offset < strlen($this->buffer));
}
$this->buffer = "";
}
public function readCompressed(string $buffer) : void{
$this->read(zlib_decode($buffer));
}
/**
* @return bool|string
*/
public function write(){
$this->offset = 0;
$this->buffer = "";
if($this->data instanceof CompoundTag){
$this->writeTag($this->data);
return $this->buffer;
}elseif(is_array($this->data)){
foreach($this->data as $tag){
$this->writeTag($tag);
}
return $this->buffer;
}
return false;
}
public function writeCompressed(int $compression = ZLIB_ENCODING_GZIP, int $level = 7){
if(($write = $this->write()) !== false){
return zlib_encode($write, $compression, $level);
}
return false;
}
public function readTag() : Tag{
if($this->feof()){
return new EndTag();
}
$tagType = $this->getByte();
$tag = NBT::createTag($tagType);
if($tag instanceof NamedTag){
$tag->setName($this->getString());
$tag->read($this);
}
return $tag;
}
public function writeTag(Tag $tag) : void{
$this->putByte($tag->getType());
if($tag instanceof NamedTag){
$this->putString($tag->getName());
}
$tag->write($this);
}
public function getByte() : int{
return Binary::readByte($this->get(1));
}
public function getSignedByte() : int{
return Binary::readSignedByte($this->get(1));
}
public function putByte(int $v) : void{
$this->buffer .= Binary::writeByte($v);
}
abstract public function getShort() : int;
abstract public function getSignedShort() : int;
abstract public function putShort(int $v) : void;
abstract public function getInt() : int;
abstract public function putInt(int $v) : void;
abstract public function getLong() : int;
abstract public function putLong(int $v) : void;
abstract public function getFloat() : float;
abstract public function putFloat(float $v) : void;
abstract public function getDouble() : float;
abstract public function putDouble(float $v) : void;
public function getString() : string{
return $this->get($this->getShort());
}
public function putString(string $v) : void{
$this->putShort(strlen($v));
$this->put($v);
}
abstract public function getIntArray() : array;
abstract public function putIntArray(array $array) : void;
public function getArray() : array{
$data = [];
self::toArray($data, $this->data);
return $data;
}
private static function toArray(array &$data, Tag $tag) : void{
/** @var CompoundTag[]|ListTag[]|IntArrayTag[] $tag */
foreach($tag as $key => $value){
if($value instanceof CompoundTag or $value instanceof ListTag or $value instanceof IntArrayTag){
$data[$key] = [];
self::toArray($data[$key], $value);
}else{
$data[$key] = $value->getValue();
}
}
}
public static function fromArrayGuesser(string $key, $value) : ?NamedTag{
if(is_int($value)){
return new IntTag($key, $value);
}elseif(is_float($value)){
return new FloatTag($key, $value);
}elseif(is_string($value)){
return new StringTag($key, $value);
}elseif(is_bool($value)){
return new ByteTag($key, $value ? 1 : 0);
}
return null;
}
private static function fromArray(Tag $tag, array $data, callable $guesser) : void{
foreach($data as $key => $value){
if(is_array($value)){
$isNumeric = true;
$isIntArray = true;
foreach($value as $k => $v){
if(!is_numeric($k)){
$isNumeric = false;
break;
}elseif(!is_int($v)){
$isIntArray = false;
}
}
$tag{$key} = $isNumeric ? ($isIntArray ? new IntArrayTag($key, []) : new ListTag($key, [])) : new CompoundTag($key, []);
self::fromArray($tag->{$key}, $value, $guesser);
}else{
$v = call_user_func($guesser, $key, $value);
if($v instanceof Tag){
$tag{$key} = $v;
}
}
}
}
public function setArray(array $data, callable $guesser = null) : void{
$this->data = new CompoundTag("", []);
self::fromArray($this->data, $data, $guesser ?? [self::class, "fromArrayGuesser"]);
}
/**
* @return CompoundTag|array
*/
public function getData(){
return $this->data;
}
/**
* @param CompoundTag|array $data
*/
public function setData($data) : void{
$this->data = $data;
}
}

View File

@ -0,0 +1,74 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\nbt;
#ifndef COMPILE
use pocketmine\utils\Binary;
#endif
#include <rules/NBT.h>
class NetworkLittleEndianNBTStream extends LittleEndianNBTStream{
public function getInt() : int{
return Binary::readVarInt($this->buffer, $this->offset);
}
public function putInt(int $v) : void{
$this->put(Binary::writeVarInt($v));
}
public function getLong() : int{
return Binary::readVarLong($this->buffer, $this->offset);
}
public function putLong(int $v) : void{
$this->put(Binary::writeVarLong($v));
}
public function getString() : string{
return $this->get(Binary::readUnsignedVarInt($this->buffer, $this->offset));
}
public function putString(string $v) : void{
$this->put(Binary::writeUnsignedVarInt(strlen($v)) . $v);
}
public function getIntArray() : array{
$len = $this->getInt(); //varint
$ret = [];
for($i = 0; $i < $len; ++$i){
$ret[] = $this->getInt(); //varint
}
return $ret;
}
public function putIntArray(array $array) : void{
$this->putInt(count($array)); //varint
foreach($array as $v){
$this->putInt($v); //varint
}
}
}

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\nbt\tag;
use pocketmine\nbt\NBT;
use pocketmine\nbt\NBTStream;
#include <rules/NBT.h>
@ -43,12 +44,12 @@ class ByteArrayTag extends NamedTag{
return NBT::TAG_ByteArray;
}
public function read(NBT $nbt, bool $network = false) : void{
$this->value = $nbt->get($nbt->getInt($network));
public function read(NBTStream $nbt) : void{
$this->value = $nbt->get($nbt->getInt());
}
public function write(NBT $nbt, bool $network = false) : void{
$nbt->putInt(strlen($this->value), $network);
public function write(NBTStream $nbt) : void{
$nbt->putInt(strlen($this->value));
$nbt->put($this->value);
}

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\nbt\tag;
use pocketmine\nbt\NBT;
use pocketmine\nbt\NBTStream;
#include <rules/NBT.h>
@ -43,11 +44,11 @@ class ByteTag extends NamedTag{
return NBT::TAG_Byte;
}
public function read(NBT $nbt, bool $network = false) : void{
public function read(NBTStream $nbt) : void{
$this->value = $nbt->getSignedByte();
}
public function write(NBT $nbt, bool $network = false) : void{
public function write(NBTStream $nbt) : void{
$nbt->putByte($this->value);
}

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\nbt\tag;
use pocketmine\nbt\NBT;
use pocketmine\nbt\NBTStream;
#include <rules/NBT.h>
@ -427,23 +428,23 @@ class CompoundTag extends NamedTag implements \ArrayAccess{
return NBT::TAG_Compound;
}
public function read(NBT $nbt, bool $network = false) : void{
public function read(NBTStream $nbt) : void{
$this->value = [];
do{
$tag = $nbt->readTag($network);
$tag = $nbt->readTag();
if($tag instanceof NamedTag and $tag->__name !== ""){
$this->{$tag->__name} = $tag;
}
}while(!($tag instanceof EndTag) and !$nbt->feof());
}
public function write(NBT $nbt, bool $network = false) : void{
public function write(NBTStream $nbt) : void{
foreach($this as $tag){
if($tag instanceof Tag and !($tag instanceof EndTag)){
$nbt->writeTag($tag, $network);
$nbt->writeTag($tag);
}
}
$nbt->writeTag(new EndTag, $network);
$nbt->writeTag(new EndTag);
}
public function __toString(){

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\nbt\tag;
use pocketmine\nbt\NBT;
use pocketmine\nbt\NBTStream;
#include <rules/NBT.h>
@ -43,11 +44,11 @@ class DoubleTag extends NamedTag{
return NBT::TAG_Double;
}
public function read(NBT $nbt, bool $network = false) : void{
public function read(NBTStream $nbt) : void{
$this->value = $nbt->getDouble();
}
public function write(NBT $nbt, bool $network = false) : void{
public function write(NBTStream $nbt) : void{
$nbt->putDouble($this->value);
}

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\nbt\tag;
use pocketmine\nbt\NBT;
use pocketmine\nbt\NBTStream;
class EndTag extends Tag{
@ -31,11 +32,11 @@ class EndTag extends Tag{
return NBT::TAG_End;
}
public function read(NBT $nbt, bool $network = false) : void{
public function read(NBTStream $nbt) : void{
}
public function write(NBT $nbt, bool $network = false) : void{
public function write(NBTStream $nbt) : void{
}
}

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\nbt\tag;
use pocketmine\nbt\NBT;
use pocketmine\nbt\NBTStream;
#include <rules/NBT.h>
@ -43,11 +44,11 @@ class FloatTag extends NamedTag{
return NBT::TAG_Float;
}
public function read(NBT $nbt, bool $network = false) : void{
public function read(NBTStream $nbt) : void{
$this->value = $nbt->getFloat();
}
public function write(NBT $nbt, bool $network = false) : void{
public function write(NBTStream $nbt) : void{
$nbt->putFloat($this->value);
}

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\nbt\tag;
use pocketmine\nbt\NBT;
use pocketmine\nbt\NBTStream;
#include <rules/NBT.h>
@ -43,14 +44,12 @@ class IntArrayTag extends NamedTag{
return NBT::TAG_IntArray;
}
public function read(NBT $nbt, bool $network = false) : void{
$size = $nbt->getInt($network);
$this->value = array_values(unpack($nbt->endianness === NBT::LITTLE_ENDIAN ? "V*" : "N*", $nbt->get($size * 4)));
public function read(NBTStream $nbt) : void{
$this->value = $nbt->getIntArray();
}
public function write(NBT $nbt, bool $network = false) : void{
$nbt->putInt(count($this->value), $network);
$nbt->put(pack($nbt->endianness === NBT::LITTLE_ENDIAN ? "V*" : "N*", ...$this->value));
public function write(NBTStream $nbt) : void{
$nbt->putIntArray($this->value);
}
public function __toString(){

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\nbt\tag;
use pocketmine\nbt\NBT;
use pocketmine\nbt\NBTStream;
#include <rules/NBT.h>
@ -41,12 +42,12 @@ class IntTag extends NamedTag{
return NBT::TAG_Int;
}
public function read(NBT $nbt, bool $network = false) : void{
$this->value = $nbt->getInt($network);
public function read(NBTStream $nbt) : void{
$this->value = $nbt->getInt();
}
public function write(NBT $nbt, bool $network = false) : void{
$nbt->putInt($this->value, $network);
public function write(NBTStream $nbt) : void{
$nbt->putInt($this->value);
}
/**

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\nbt\tag;
use pocketmine\nbt\NBT;
use pocketmine\nbt\NBTStream;
#include <rules/NBT.h>
@ -163,20 +164,20 @@ class ListTag extends NamedTag implements \ArrayAccess, \Countable{
return $this->tagType;
}
public function read(NBT $nbt, bool $network = false) : void{
public function read(NBTStream $nbt) : void{
$this->value = [];
$this->tagType = $nbt->getByte();
$size = $nbt->getInt($network);
$size = $nbt->getInt();
$tagBase = NBT::createTag($this->tagType);
for($i = 0; $i < $size and !$nbt->feof(); ++$i){
$tag = clone $tagBase;
$tag->read($nbt, $network);
$tag->read($nbt);
$this->{$i} = $tag;
}
}
public function write(NBT $nbt, bool $network = false) : void{
public function write(NBTStream $nbt) : void{
if($this->tagType === NBT::TAG_End){ //previously empty list, try detecting type from tag children
$id = NBT::TAG_End;
foreach($this as $tag){
@ -200,9 +201,9 @@ class ListTag extends NamedTag implements \ArrayAccess, \Countable{
$tags[] = $tag;
}
}
$nbt->putInt(count($tags), $network);
$nbt->putInt(count($tags));
foreach($tags as $tag){
$tag->write($nbt, $network);
$tag->write($nbt);
}
}

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\nbt\tag;
use pocketmine\nbt\NBT;
use pocketmine\nbt\NBTStream;
#include <rules/NBT.h>
@ -43,12 +44,12 @@ class LongTag extends NamedTag{
return NBT::TAG_Long;
}
public function read(NBT $nbt, bool $network = false) : void{
$this->value = $nbt->getLong($network);
public function read(NBTStream $nbt) : void{
$this->value = $nbt->getLong();
}
public function write(NBT $nbt, bool $network = false) : void{
$nbt->putLong($this->value, $network);
public function write(NBTStream $nbt) : void{
$nbt->putLong($this->value);
}
/**

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\nbt\tag;
use pocketmine\nbt\NBT;
use pocketmine\nbt\NBTStream;
#include <rules/NBT.h>
@ -43,11 +44,11 @@ class ShortTag extends NamedTag{
return NBT::TAG_Short;
}
public function read(NBT $nbt, bool $network = false) : void{
public function read(NBTStream $nbt) : void{
$this->value = $nbt->getSignedShort();
}
public function write(NBT $nbt, bool $network = false) : void{
public function write(NBTStream $nbt) : void{
$nbt->putShort($this->value);
}

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\nbt\tag;
use pocketmine\nbt\NBT;
use pocketmine\nbt\NBTStream;
#include <rules/NBT.h>
@ -43,12 +44,12 @@ class StringTag extends NamedTag{
return NBT::TAG_String;
}
public function read(NBT $nbt, bool $network = false) : void{
$this->value = $nbt->getString($network);
public function read(NBTStream $nbt) : void{
$this->value = $nbt->getString();
}
public function write(NBT $nbt, bool $network = false) : void{
$nbt->putString($this->value, $network);
public function write(NBTStream $nbt) : void{
$nbt->putString($this->value);
}
/**

View File

@ -26,7 +26,7 @@ declare(strict_types=1);
*/
namespace pocketmine\nbt\tag;
use pocketmine\nbt\NBT;
use pocketmine\nbt\NBTStream;
abstract class Tag extends \stdClass{
@ -42,9 +42,9 @@ abstract class Tag extends \stdClass{
$this->value = $value;
}
abstract public function write(NBT $nbt, bool $network = false) : void;
abstract public function write(NBTStream $nbt) : void;
abstract public function read(NBT $nbt, bool $network = false) : void;
abstract public function read(NBTStream $nbt) : void;
public function __toString(){
return (string) $this->value;

View File

@ -25,6 +25,7 @@ namespace pocketmine\tile;
use pocketmine\level\Level;
use pocketmine\nbt\NBT;
use pocketmine\nbt\NetworkLittleEndianNBTStream;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\protocol\BlockEntityDataPacket;
use pocketmine\Player;
@ -92,11 +93,11 @@ abstract class Spawnable extends Tile{
final public function getSerializedSpawnCompound() : string{
if($this->spawnCompoundCache === null){
if(self::$nbtWriter === null){
self::$nbtWriter = new NBT(NBT::LITTLE_ENDIAN);
self::$nbtWriter = new NetworkLittleEndianNBTStream();
}
self::$nbtWriter->setData($this->getSpawnCompound());
$this->spawnCompoundCache = self::$nbtWriter->write(true);
$this->spawnCompoundCache = self::$nbtWriter->write();
}
return $this->spawnCompoundCache;

@ -1 +1 @@
Subproject commit 86961f81970a49f8c25e18d0d414961d7142a84d
Subproject commit 316fd5606fef1191e7f228d3156dd1518c33aba4