diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index e73825f44..ed2afbde8 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -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); diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index e8ac70fb2..c8a14d98f 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -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()); diff --git a/src/pocketmine/item/Item.php b/src/pocketmine/item/Item.php index e1b477d2d..5aef7797a 100644 --- a/src/pocketmine/item/Item.php +++ b/src/pocketmine/item/Item.php @@ -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); diff --git a/src/pocketmine/level/format/io/BaseLevelProvider.php b/src/pocketmine/level/format/io/BaseLevelProvider.php index 806f1f0db..62d757375 100644 --- a/src/pocketmine/level/format/io/BaseLevelProvider.php +++ b/src/pocketmine/level/format/io/BaseLevelProvider.php @@ -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 ])); diff --git a/src/pocketmine/level/format/io/leveldb/LevelDB.php b/src/pocketmine/level/format/io/leveldb/LevelDB.php index d360268c5..b59021bc9 100644 --- a/src/pocketmine/level/format/io/leveldb/LevelDB.php +++ b/src/pocketmine/level/format/io/leveldb/LevelDB.php @@ -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{ diff --git a/src/pocketmine/level/format/io/region/Anvil.php b/src/pocketmine/level/format/io/region/Anvil.php index 7e6b27b43..e6e3ac085 100644 --- a/src/pocketmine/level/format/io/region/Anvil.php +++ b/src/pocketmine/level/format/io/region/Anvil.php @@ -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); diff --git a/src/pocketmine/level/format/io/region/McRegion.php b/src/pocketmine/level/format/io/region/McRegion.php index addfc9b33..d073c86b0 100644 --- a/src/pocketmine/level/format/io/region/McRegion.php +++ b/src/pocketmine/level/format/io/region/McRegion.php @@ -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 ])); diff --git a/src/pocketmine/nbt/BigEndianNBTStream.php b/src/pocketmine/nbt/BigEndianNBTStream.php new file mode 100644 index 000000000..d86d850d8 --- /dev/null +++ b/src/pocketmine/nbt/BigEndianNBTStream.php @@ -0,0 +1,87 @@ + + +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)); + } +} diff --git a/src/pocketmine/nbt/LittleEndianNBTStream.php b/src/pocketmine/nbt/LittleEndianNBTStream.php new file mode 100644 index 000000000..c51c85ce1 --- /dev/null +++ b/src/pocketmine/nbt/LittleEndianNBTStream.php @@ -0,0 +1,87 @@ + + +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)); + } +} diff --git a/src/pocketmine/nbt/NBT.php b/src/pocketmine/nbt/NBT.php index debc56946..3827884e6 100644 --- a/src/pocketmine/nbt/NBT.php +++ b/src/pocketmine/nbt/NBT.php @@ -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 - -/** - * 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; - } - } diff --git a/src/pocketmine/nbt/NBTStream.php b/src/pocketmine/nbt/NBTStream.php new file mode 100644 index 000000000..8531fb34e --- /dev/null +++ b/src/pocketmine/nbt/NBTStream.php @@ -0,0 +1,271 @@ + + +/** + * 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; + } +} diff --git a/src/pocketmine/nbt/NetworkLittleEndianNBTStream.php b/src/pocketmine/nbt/NetworkLittleEndianNBTStream.php new file mode 100644 index 000000000..4bb583783 --- /dev/null +++ b/src/pocketmine/nbt/NetworkLittleEndianNBTStream.php @@ -0,0 +1,74 @@ + + +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 + } + } +} diff --git a/src/pocketmine/nbt/tag/ByteArrayTag.php b/src/pocketmine/nbt/tag/ByteArrayTag.php index 8d4555d33..0023958c7 100644 --- a/src/pocketmine/nbt/tag/ByteArrayTag.php +++ b/src/pocketmine/nbt/tag/ByteArrayTag.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\nbt\tag; use pocketmine\nbt\NBT; +use pocketmine\nbt\NBTStream; #include @@ -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); } diff --git a/src/pocketmine/nbt/tag/ByteTag.php b/src/pocketmine/nbt/tag/ByteTag.php index aca900764..dd93a7864 100644 --- a/src/pocketmine/nbt/tag/ByteTag.php +++ b/src/pocketmine/nbt/tag/ByteTag.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\nbt\tag; use pocketmine\nbt\NBT; +use pocketmine\nbt\NBTStream; #include @@ -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); } diff --git a/src/pocketmine/nbt/tag/CompoundTag.php b/src/pocketmine/nbt/tag/CompoundTag.php index 1322e7472..e17f1f495 100644 --- a/src/pocketmine/nbt/tag/CompoundTag.php +++ b/src/pocketmine/nbt/tag/CompoundTag.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\nbt\tag; use pocketmine\nbt\NBT; +use pocketmine\nbt\NBTStream; #include @@ -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(){ diff --git a/src/pocketmine/nbt/tag/DoubleTag.php b/src/pocketmine/nbt/tag/DoubleTag.php index bc3f1cf57..1ce8f91d5 100644 --- a/src/pocketmine/nbt/tag/DoubleTag.php +++ b/src/pocketmine/nbt/tag/DoubleTag.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\nbt\tag; use pocketmine\nbt\NBT; +use pocketmine\nbt\NBTStream; #include @@ -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); } diff --git a/src/pocketmine/nbt/tag/EndTag.php b/src/pocketmine/nbt/tag/EndTag.php index c063196d4..11d692131 100644 --- a/src/pocketmine/nbt/tag/EndTag.php +++ b/src/pocketmine/nbt/tag/EndTag.php @@ -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{ } } diff --git a/src/pocketmine/nbt/tag/FloatTag.php b/src/pocketmine/nbt/tag/FloatTag.php index 4855bd916..41424f7fe 100644 --- a/src/pocketmine/nbt/tag/FloatTag.php +++ b/src/pocketmine/nbt/tag/FloatTag.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\nbt\tag; use pocketmine\nbt\NBT; +use pocketmine\nbt\NBTStream; #include @@ -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); } diff --git a/src/pocketmine/nbt/tag/IntArrayTag.php b/src/pocketmine/nbt/tag/IntArrayTag.php index bb1c9e8ea..30c61d4f9 100644 --- a/src/pocketmine/nbt/tag/IntArrayTag.php +++ b/src/pocketmine/nbt/tag/IntArrayTag.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\nbt\tag; use pocketmine\nbt\NBT; +use pocketmine\nbt\NBTStream; #include @@ -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(){ diff --git a/src/pocketmine/nbt/tag/IntTag.php b/src/pocketmine/nbt/tag/IntTag.php index 093cd9342..ed38d756a 100644 --- a/src/pocketmine/nbt/tag/IntTag.php +++ b/src/pocketmine/nbt/tag/IntTag.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\nbt\tag; use pocketmine\nbt\NBT; +use pocketmine\nbt\NBTStream; #include @@ -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); } /** diff --git a/src/pocketmine/nbt/tag/ListTag.php b/src/pocketmine/nbt/tag/ListTag.php index 6b3db2636..b7f723c88 100644 --- a/src/pocketmine/nbt/tag/ListTag.php +++ b/src/pocketmine/nbt/tag/ListTag.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\nbt\tag; use pocketmine\nbt\NBT; +use pocketmine\nbt\NBTStream; #include @@ -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); } } diff --git a/src/pocketmine/nbt/tag/LongTag.php b/src/pocketmine/nbt/tag/LongTag.php index 5fe001327..e435afefc 100644 --- a/src/pocketmine/nbt/tag/LongTag.php +++ b/src/pocketmine/nbt/tag/LongTag.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\nbt\tag; use pocketmine\nbt\NBT; +use pocketmine\nbt\NBTStream; #include @@ -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); } /** diff --git a/src/pocketmine/nbt/tag/ShortTag.php b/src/pocketmine/nbt/tag/ShortTag.php index f3b5b5705..6eabeb222 100644 --- a/src/pocketmine/nbt/tag/ShortTag.php +++ b/src/pocketmine/nbt/tag/ShortTag.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\nbt\tag; use pocketmine\nbt\NBT; +use pocketmine\nbt\NBTStream; #include @@ -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); } diff --git a/src/pocketmine/nbt/tag/StringTag.php b/src/pocketmine/nbt/tag/StringTag.php index f0ad886b8..53deb5d87 100644 --- a/src/pocketmine/nbt/tag/StringTag.php +++ b/src/pocketmine/nbt/tag/StringTag.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\nbt\tag; use pocketmine\nbt\NBT; +use pocketmine\nbt\NBTStream; #include @@ -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); } /** diff --git a/src/pocketmine/nbt/tag/Tag.php b/src/pocketmine/nbt/tag/Tag.php index 74e4d7b78..f4233378e 100644 --- a/src/pocketmine/nbt/tag/Tag.php +++ b/src/pocketmine/nbt/tag/Tag.php @@ -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; diff --git a/src/pocketmine/tile/Spawnable.php b/src/pocketmine/tile/Spawnable.php index 598f6211c..427512577 100644 --- a/src/pocketmine/tile/Spawnable.php +++ b/src/pocketmine/tile/Spawnable.php @@ -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; diff --git a/tests/preprocessor b/tests/preprocessor index 86961f819..316fd5606 160000 --- a/tests/preprocessor +++ b/tests/preprocessor @@ -1 +1 @@ -Subproject commit 86961f81970a49f8c25e18d0d414961d7142a84d +Subproject commit 316fd5606fef1191e7f228d3156dd1518c33aba4