NBT: Split up concerns of endianness and varint NBT into their own classes, separate stream handling from NBT class

The remaining methods, constants and fields in the NBT class now pertain to generic NBT functionality (except for the matchList()/matchTree() methods, but that's a job for another time). All NBT I/O specific logic has now been moved to NBTStream and its descendents.
This commit is contained in:
Dylan K. Taylor 2017-11-18 16:44:21 +00:00
parent 63edcb8934
commit 965c19375f
26 changed files with 588 additions and 342 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,83 @@
<?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\utils\Binary;
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($v){
$this->buffer .= Binary::writeShort($v);
}
public function getInt() : int{
return Binary::readInt($this->get(4));
}
public function putInt($v){
$this->buffer .= Binary::writeInt($v);
}
public function getLong() : int{
return Binary::readLong($this->get(8));
}
public function putLong($v){
$this->buffer .= Binary::writeLong($v);
}
public function getFloat() : float{
return Binary::readFloat($this->get(4));
}
public function putFloat($v){
$this->buffer .= Binary::writeFloat($v);
}
public function getDouble() : float{
return Binary::readDouble($this->get(8));
}
public function putDouble($v){
$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,83 @@
<?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\utils\Binary;
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($v){
$this->put(Binary::writeLShort($v));
}
public function getInt() : int{
return Binary::readLInt($this->get(4));
}
public function putInt($v){
$this->put(Binary::writeLInt($v));
}
public function getLong() : int{
return Binary::readLLong($this->get(8));
}
public function putLong($v){
$this->put(Binary::writeLLong($v));
}
public function getFloat() : float{
return Binary::readLFloat($this->get(4));
}
public function putFloat($v){
$this->put(Binary::writeLFloat($v));
}
public function getDouble() : float{
return Binary::readLDouble($this->get(8));
}
public function putDouble($v){
$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,11 +57,6 @@ class NBT{
public const TAG_Compound = 10;
public const TAG_IntArray = 11;
public $buffer;
public $offset;
public $endianness;
private $data;
/**
* @param int $type
*
@ -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,267 @@
<?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;
use pocketmine\utils\Binary;
/**
* Base Named Binary Tag encoder/decoder
*/
abstract class NBTStream{
public $buffer;
public $offset;
private $data;
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(){
$this->offset = 0;
$this->buffer = "";
}
public function read($buffer, $doMultiple = false){
$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($buffer){
$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($compression = ZLIB_ENCODING_GZIP, $level = 7){
if(($write = $this->write()) !== false){
return zlib_encode($write, $compression, $level);
}
return false;
}
public function readTag(){
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){
$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($v){
$this->buffer .= Binary::writeByte($v);
}
abstract public function getShort() : int;
abstract public function getSignedShort() : int;
abstract public function putShort($v);
abstract public function getInt() : int;
abstract public function putInt($v);
abstract public function getLong() : int;
abstract public function putLong($v);
abstract public function getFloat() : float;
abstract public function putFloat($v);
abstract public function getDouble() : float;
abstract public function putDouble($v);
public function getString(){
return $this->get($this->getShort());
}
public function putString($v){
$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){
/** @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,70 @@
<?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\utils\Binary;
class NetworkLittleEndianNBTStream extends LittleEndianNBTStream{
public function getInt() : int{
return Binary::readVarInt($this->buffer, $this->offset);
}
public function putInt($v){
$this->put(Binary::writeVarInt($v));
}
public function getLong() : int{
return Binary::readVarLong($this->buffer, $this->offset);
}
public function putLong($v){
$this->put(Binary::writeVarLong($v));
}
public function getString(){
return $this->get(Binary::readUnsignedVarInt($this->buffer, $this->offset));
}
public function putString($v){
$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;