Make tiles less dependent on runtime NBT, use properties instead

This will ultimately culminate in the complete removal of runtime NBT, so plugins should also follow these steps if they have custom data.
This commit is contained in:
Dylan K. Taylor 2018-06-03 12:50:16 +01:00
parent 1bb0337420
commit 6aaaaefd2f
8 changed files with 153 additions and 104 deletions

View File

@ -92,8 +92,8 @@ class StandingBanner extends Transparent{
$tile = $this->level->getTile($this);
$drop = ItemFactory::get(Item::BANNER, ($tile instanceof TileBanner ? $tile->getBaseColor() : 0));
if($tile instanceof TileBanner and ($patterns = $tile->namedtag->getListTag(TileBanner::TAG_PATTERNS)) !== null and !$patterns->empty()){
$drop->setNamedTagEntry($patterns);
if($tile instanceof TileBanner and !($patterns = $tile->getPatterns())->empty()){
$drop->setNamedTagEntry(clone $patterns);
}
return [$drop];

View File

@ -99,19 +99,30 @@ class Banner extends Spawnable implements Nameable{
public const COLOR_ORANGE = 14;
public const COLOR_WHITE = 15;
/** @var int */
private $baseColor;
/**
* @var ListTag
* TODO: break this down further and remove runtime NBT from here entirely
*/
private $patterns;
public function __construct(Level $level, CompoundTag $nbt){
if(!$nbt->hasTag(self::TAG_BASE, IntTag::class)){
$nbt->setInt(self::TAG_BASE, 0);
}
if(!$nbt->hasTag(self::TAG_PATTERNS, ListTag::class)){
$nbt->setTag(new ListTag(self::TAG_PATTERNS));
}
$this->baseColor = $nbt->getInt(self::TAG_BASE, self::COLOR_BLACK, true);
$this->patterns = $nbt->getListTag(self::TAG_PATTERNS) ?? new ListTag(self::TAG_PATTERNS);
$nbt->removeTag(self::TAG_BASE, self::TAG_PATTERNS);
parent::__construct($level, $nbt);
}
public function saveNBT() : void{
parent::saveNBT();
$this->namedtag->setInt(self::TAG_BASE, $this->baseColor);
$this->namedtag->setTag($this->patterns);
}
public function addAdditionalSpawnData(CompoundTag $nbt) : void{
$nbt->setTag($this->namedtag->getTag(self::TAG_PATTERNS));
$nbt->setTag($this->namedtag->getTag(self::TAG_BASE));
$nbt->setInt(self::TAG_BASE, $this->baseColor);
$nbt->setTag($this->patterns);
$this->addNameSpawnData($nbt);
}
@ -121,7 +132,7 @@ class Banner extends Spawnable implements Nameable{
* @return int
*/
public function getBaseColor() : int{
return $this->namedtag->getInt(self::TAG_BASE, 0);
return $this->baseColor;
}
/**
@ -130,7 +141,7 @@ class Banner extends Spawnable implements Nameable{
* @param int $color
*/
public function setBaseColor(int $color) : void{
$this->namedtag->setInt(self::TAG_BASE, $color & 0x0f);
$this->baseColor = $color;
$this->onChanged();
}
@ -143,15 +154,13 @@ class Banner extends Spawnable implements Nameable{
* @return int ID of pattern.
*/
public function addPattern(string $pattern, int $color) : int{
$list = $this->namedtag->getListTag(self::TAG_PATTERNS);
assert($list !== null);
$list->push(new CompoundTag("", [
$this->patterns->push(new CompoundTag("", [
new IntTag(self::TAG_PATTERN_COLOR, $color & 0x0f),
new StringTag(self::TAG_PATTERN_NAME, $pattern)
]));
$this->onChanged();
return $list->count() - 1; //Last offset in the list
return $this->patterns->count() - 1; //Last offset in the list
}
/**
@ -162,7 +171,7 @@ class Banner extends Spawnable implements Nameable{
* @return bool
*/
public function patternExists(int $patternId) : bool{
return $this->namedtag->getListTag(self::TAG_PATTERNS)->isset($patternId);
return $this->patterns->isset($patternId);
}
/**
@ -177,9 +186,7 @@ class Banner extends Spawnable implements Nameable{
return [];
}
$list = $this->namedtag->getListTag(self::TAG_PATTERNS);
assert($list instanceof ListTag);
$patternTag = $list->get($patternId);
$patternTag = $this->patterns->get($patternId);
assert($patternTag instanceof CompoundTag);
return [
@ -202,10 +209,7 @@ class Banner extends Spawnable implements Nameable{
return false;
}
$list = $this->namedtag->getListTag(self::TAG_PATTERNS);
assert($list instanceof ListTag);
$list->set($patternId, new CompoundTag("", [
$this->patterns->set($patternId, new CompoundTag("", [
new IntTag(self::TAG_PATTERN_COLOR, $color & 0x0f),
new StringTag(self::TAG_PATTERN_NAME, $pattern)
]));
@ -226,10 +230,7 @@ class Banner extends Spawnable implements Nameable{
return false;
}
$list = $this->namedtag->getListTag(self::TAG_PATTERNS);
if($list !== null){
$list->remove($patternId);
}
$this->patterns->remove($patternId);
$this->onChanged();
return true;
@ -259,7 +260,14 @@ class Banner extends Spawnable implements Nameable{
* @return int
*/
public function getPatternCount() : int{
return $this->namedtag->getListTag(self::TAG_PATTERNS)->count();
return $this->patterns->count();
}
/**
* @return ListTag
*/
public function getPatterns() : ListTag{
return $this->patterns;
}
protected static function createAdditionalNBT(CompoundTag $nbt, Vector3 $pos, ?int $face = null, ?Item $item = null, ?Player $player = null) : void{

View File

@ -27,34 +27,42 @@ namespace pocketmine\tile;
use pocketmine\item\Item;
use pocketmine\level\Level;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\Player;
class Bed extends Spawnable{
public const TAG_COLOR = "color";
/** @var int */
private $color = 14; //default to old red
public function __construct(Level $level, CompoundTag $nbt){
if(!$nbt->hasTag(self::TAG_COLOR, ByteTag::class)){ //TODO: check PC format
$nbt->setByte(self::TAG_COLOR, 14, true); //default to old red
}
$this->color = $nbt->getByte(self::TAG_COLOR, 14, true);
$nbt->removeTag(self::TAG_COLOR);
parent::__construct($level, $nbt);
}
public function getColor() : int{
return $this->namedtag->getByte(self::TAG_COLOR);
return $this->color;
}
public function setColor(int $color){
$this->namedtag->setByte(self::TAG_COLOR, $color & 0x0f);
$this->color = $color & 0xf;
$this->onChanged();
}
public function addAdditionalSpawnData(CompoundTag $nbt) : void{
$nbt->setTag($this->namedtag->getTag(self::TAG_COLOR));
$nbt->setByte(self::TAG_COLOR, $this->color);
}
public function saveNBT() : void{
parent::saveNBT();
$this->namedtag->setByte(self::TAG_COLOR, $this->color);
}
protected static function createAdditionalNBT(CompoundTag $nbt, Vector3 $pos, ?int $face = null, ?Item $item = null, ?Player $player = null) : void{
$nbt->setByte(self::TAG_COLOR, $item !== null ? $item->getDamage() : 14); //default red
if($item !== null){
$nbt->setByte(self::TAG_COLOR, $item->getDamage());
}
}
}

View File

@ -28,6 +28,7 @@ use pocketmine\inventory\DoubleChestInventory;
use pocketmine\inventory\InventoryHolder;
use pocketmine\level\Level;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag;
class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
use NameableTrait {
@ -44,8 +45,19 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
/** @var DoubleChestInventory */
protected $doubleInventory = null;
/** @var int|null */
private $pairX;
/** @var int|null */
private $pairZ;
public function __construct(Level $level, CompoundTag $nbt){
if($nbt->hasTag(self::TAG_PAIRX, IntTag::class) and $nbt->hasTag(self::TAG_PAIRZ, IntTag::class)){
$this->pairX = $nbt->getInt(self::TAG_PAIRX);
$this->pairZ = $nbt->getInt(self::TAG_PAIRZ);
}
$nbt->removeTag(self::TAG_PAIRX, self::TAG_PAIRZ);
parent::__construct($level, $nbt);
$this->inventory = new ChestInventory($this);
$this->loadItems();
}
@ -68,6 +80,12 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
public function saveNBT() : void{
parent::saveNBT();
if($this->isPaired()){
$this->namedtag->setInt(self::TAG_PAIRX, $this->pairX);
$this->namedtag->setInt(self::TAG_PAIRZ, $this->pairZ);
}else{
$this->namedtag->removeTag(self::TAG_PAIRX, self::TAG_PAIRZ);
}
$this->saveItems();
}
@ -89,7 +107,7 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
}
protected function checkPairing(){
if($this->isPaired() and !$this->getLevel()->isChunkLoaded($this->namedtag->getInt(self::TAG_PAIRX) >> 4, $this->namedtag->getInt(self::TAG_PAIRZ) >> 4)){
if($this->isPaired() and !$this->getLevel()->isChunkLoaded($this->pairX >> 4, $this->pairZ >> 4)){
//paired to a tile in an unloaded chunk
$this->doubleInventory = null;
@ -107,7 +125,7 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
}
}else{
$this->doubleInventory = null;
$this->namedtag->removeTag(self::TAG_PAIRX, self::TAG_PAIRZ);
$this->pairX = $this->pairZ = null;
}
}
@ -119,7 +137,7 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
}
public function isPaired(){
return $this->namedtag->hasTag(self::TAG_PAIRX) and $this->namedtag->hasTag(self::TAG_PAIRZ);
return $this->pairX !== null and $this->pairZ !== null;
}
/**
@ -127,7 +145,7 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
*/
public function getPair() : ?Chest{
if($this->isPaired()){
$tile = $this->getLevel()->getTileAt($this->namedtag->getInt(self::TAG_PAIRX), $this->y, $this->namedtag->getInt(self::TAG_PAIRZ));
$tile = $this->getLevel()->getTileAt($this->pairX, $this->y, $this->pairZ);
if($tile instanceof Chest){
return $tile;
}
@ -151,11 +169,11 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
}
private function createPair(Chest $tile){
$this->namedtag->setInt(self::TAG_PAIRX, $tile->x);
$this->namedtag->setInt(self::TAG_PAIRZ, $tile->z);
$this->pairX = $tile->x;
$this->pairZ = $tile->z;
$tile->namedtag->setInt(self::TAG_PAIRX, $this->x);
$tile->namedtag->setInt(self::TAG_PAIRZ, $this->z);
$tile->pairX = $this->x;
$tile->pairZ = $this->z;
}
public function unpair(){
@ -164,12 +182,12 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
}
$tile = $this->getPair();
$this->namedtag->removeTag(self::TAG_PAIRX, self::TAG_PAIRZ);
$this->pairX = $this->pairZ = null;
$this->onChanged();
if($tile instanceof Chest){
$tile->namedtag->removeTag(self::TAG_PAIRX, self::TAG_PAIRZ);
$tile->pairX = $tile->pairZ = null;
$tile->checkPairing();
$tile->onChanged();
}
@ -180,8 +198,8 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
public function addAdditionalSpawnData(CompoundTag $nbt) : void{
if($this->isPaired()){
$nbt->setTag($this->namedtag->getTag(self::TAG_PAIRX));
$nbt->setTag($this->namedtag->getTag(self::TAG_PAIRZ));
$nbt->setInt(self::TAG_PAIRX, $this->pairX);
$nbt->setInt(self::TAG_PAIRZ, $this->pairZ);
}
$this->addNameSpawnData($nbt);

View File

@ -27,24 +27,27 @@ use pocketmine\item\Item;
use pocketmine\item\ItemFactory;
use pocketmine\level\Level;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\ShortTag;
class FlowerPot extends Spawnable{
public const TAG_ITEM = "item";
public const TAG_ITEM_DATA = "mData";
/** @var Item */
private $item;
public function __construct(Level $level, CompoundTag $nbt){
//TODO: check PC format
if(!$nbt->hasTag(self::TAG_ITEM, ShortTag::class)){
$nbt->setShort(self::TAG_ITEM, 0, true);
}
if(!$nbt->hasTag(self::TAG_ITEM_DATA, IntTag::class)){
$nbt->setInt(self::TAG_ITEM_DATA, 0, true);
}
$this->item = ItemFactory::get($nbt->getShort(self::TAG_ITEM, 0, true), $nbt->getInt(self::TAG_ITEM_DATA, 0, true), 1);
$nbt->removeTag(self::TAG_ITEM, self::TAG_ITEM_DATA);
parent::__construct($level, $nbt);
}
public function saveNBT() : void{
$this->namedtag->setShort(self::TAG_ITEM, $this->item->getId());
$this->namedtag->setInt(self::TAG_ITEM_DATA, $this->item->getDamage());
}
public function canAddItem(Item $item) : bool{
if(!$this->isEmpty()){
return false;
@ -69,12 +72,11 @@ class FlowerPot extends Spawnable{
}
public function getItem() : Item{
return ItemFactory::get($this->namedtag->getShort(self::TAG_ITEM), $this->namedtag->getInt(self::TAG_ITEM_DATA), 1);
return clone $this->item;
}
public function setItem(Item $item){
$this->namedtag->setShort(self::TAG_ITEM, $item->getId());
$this->namedtag->setInt(self::TAG_ITEM_DATA, $item->getDamage());
$this->item = clone $item;
$this->onChanged();
}
@ -87,7 +89,7 @@ class FlowerPot extends Spawnable{
}
public function addAdditionalSpawnData(CompoundTag $nbt) : void{
$nbt->setTag($this->namedtag->getTag(self::TAG_ITEM));
$nbt->setTag($this->namedtag->getTag(self::TAG_ITEM_DATA));
$nbt->setShort(self::TAG_ITEM, $this->item->getId());
$nbt->setInt(self::TAG_ITEM_DATA, $this->item->getDamage());
}
}

View File

@ -26,73 +26,78 @@ namespace pocketmine\tile;
use pocketmine\item\Item;
use pocketmine\item\ItemFactory;
use pocketmine\level\Level;
use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\FloatTag;
class ItemFrame extends Spawnable{
public const TAG_ITEM_ROTATION = "ItemRotation";
public const TAG_ITEM_DROP_CHANCE = "ItemDropChance";
public const TAG_ITEM = "Item";
public function __construct(Level $level, CompoundTag $nbt){
if(!$nbt->hasTag(self::TAG_ITEM_ROTATION, ByteTag::class)){
$nbt->setByte(self::TAG_ITEM_ROTATION, 0, true);
}
/** @var Item */
private $item;
/** @var int */
private $itemRotation;
/** @var float */
private $itemDropChance;
if(!$nbt->hasTag(self::TAG_ITEM_DROP_CHANCE, FloatTag::class)){
$nbt->setFloat(self::TAG_ITEM_DROP_CHANCE, 1.0, true);
public function __construct(Level $level, CompoundTag $nbt){
if(($itemTag = $nbt->getCompoundTag(self::TAG_ITEM)) !== null){
$this->item = Item::nbtDeserialize($itemTag);
}else{
$this->item = ItemFactory::get(Item::AIR, 0, 0);
}
$this->itemRotation = $nbt->getByte(self::TAG_ITEM_ROTATION, 0, true);
$this->itemDropChance = $nbt->getFloat(self::TAG_ITEM_DROP_CHANCE, 1.0, true);
$nbt->removeTag(self::TAG_ITEM, self::TAG_ITEM_ROTATION, self::TAG_ITEM_DROP_CHANCE);
parent::__construct($level, $nbt);
}
public function saveNBT() : void{
parent::saveNBT();
$this->namedtag->setFloat(self::TAG_ITEM_DROP_CHANCE, $this->itemDropChance);
$this->namedtag->setByte(self::TAG_ITEM_ROTATION, $this->itemRotation);
$this->namedtag->setTag($this->item->nbtSerialize(-1, self::TAG_ITEM));
}
public function hasItem() : bool{
return !$this->getItem()->isNull();
return !$this->item->isNull();
}
public function getItem() : Item{
$c = $this->namedtag->getCompoundTag(self::TAG_ITEM);
if($c !== null){
return Item::nbtDeserialize($c);
}
return ItemFactory::get(Item::AIR, 0, 0);
return clone $this->item;
}
public function setItem(Item $item = null){
if($item !== null and !$item->isNull()){
$this->namedtag->setTag($item->nbtSerialize(-1, self::TAG_ITEM));
$this->item = clone $item;
}else{
$this->namedtag->removeTag(self::TAG_ITEM);
$this->item = ItemFactory::get(Item::AIR, 0, 0);
}
$this->onChanged();
}
public function getItemRotation() : int{
return $this->namedtag->getByte(self::TAG_ITEM_ROTATION);
return $this->itemRotation;
}
public function setItemRotation(int $rotation){
$this->namedtag->setByte(self::TAG_ITEM_ROTATION, $rotation);
$this->itemRotation = $rotation;
$this->onChanged();
}
public function getItemDropChance() : float{
return $this->namedtag->getFloat(self::TAG_ITEM_DROP_CHANCE);
return $this->itemDropChance;
}
public function setItemDropChance(float $chance){
$this->namedtag->setFloat(self::TAG_ITEM_DROP_CHANCE, $chance);
$this->itemDropChance = $chance;
$this->onChanged();
}
public function addAdditionalSpawnData(CompoundTag $nbt) : void{
$nbt->setTag($this->namedtag->getTag(self::TAG_ITEM_DROP_CHANCE));
$nbt->setTag($this->namedtag->getTag(self::TAG_ITEM_ROTATION));
if($this->hasItem()){
$nbt->setTag($this->namedtag->getTag(self::TAG_ITEM));
}
$nbt->setFloat(self::TAG_ITEM_DROP_CHANCE, $this->itemDropChance);
$nbt->setByte(self::TAG_ITEM_ROTATION, $this->itemRotation);
$nbt->setTag($this->item->nbtSerialize(-1, self::TAG_ITEM));
}
}

View File

@ -26,7 +26,6 @@ namespace pocketmine\tile;
use pocketmine\item\Item;
use pocketmine\level\Level;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\Player;
@ -43,28 +42,36 @@ class Skull extends Spawnable{
public const TAG_MOUTH_MOVING = "MouthMoving"; //TAG_Byte
public const TAG_MOUTH_TICK_COUNT = "MouthTickCount"; //TAG_Int
/** @var int */
private $skullType;
/** @var int */
private $skullRotation;
public function __construct(Level $level, CompoundTag $nbt){
if(!$nbt->hasTag(self::TAG_SKULL_TYPE, ByteTag::class)){
$nbt->setByte(self::TAG_SKULL_TYPE, 0, true);
}
if(!$nbt->hasTag(self::TAG_ROT, ByteTag::class)){
$nbt->setByte(self::TAG_ROT, 0, true);
}
$this->skullType = $nbt->getByte(self::TAG_SKULL_TYPE, self::TYPE_SKELETON, true);
$this->skullRotation = $nbt->getByte(self::TAG_ROT, 0, true);
$nbt->removeTag(self::TAG_SKULL_TYPE, self::TAG_ROT);
parent::__construct($level, $nbt);
}
public function saveNBT() : void{
parent::saveNBT();
$this->namedtag->setByte(self::TAG_SKULL_TYPE, $this->skullType);
$this->namedtag->setByte(self::TAG_ROT, $this->skullRotation);
}
public function setType(int $type){
$this->namedtag->setByte(self::TAG_SKULL_TYPE, $type);
$this->skullType = $type;
$this->onChanged();
}
public function getType() : int{
return $this->namedtag->getByte(self::TAG_SKULL_TYPE);
return $this->skullType;
}
public function addAdditionalSpawnData(CompoundTag $nbt) : void{
$nbt->setTag($this->namedtag->getTag(self::TAG_SKULL_TYPE));
$nbt->setTag($this->namedtag->getTag(self::TAG_ROT));
$nbt->setByte(self::TAG_SKULL_TYPE, $this->skullType);
$nbt->setByte(self::TAG_ROT, $this->skullRotation);
}
protected static function createAdditionalNBT(CompoundTag $nbt, Vector3 $pos, ?int $face = null, ?Item $item = null, ?Player $player = null) : void{
@ -74,6 +81,6 @@ class Skull extends Spawnable{
if($face === Vector3::SIDE_UP and $player !== null){
$rot = floor(($player->yaw * 16 / 360) + 0.5) & 0x0F;
}
$nbt->setByte("Rot", $rot);
$nbt->setByte(self::TAG_ROT, $rot);
}
}

View File

@ -26,6 +26,7 @@ namespace pocketmine\tile;
use pocketmine\level\Level;
use pocketmine\nbt\NetworkLittleEndianNBTStream;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\network\mcpe\protocol\BlockEntityDataPacket;
use pocketmine\Player;
@ -105,9 +106,9 @@ abstract class Spawnable extends Tile{
final public function getSpawnCompound() : CompoundTag{
$nbt = new CompoundTag("", [
new StringTag(self::TAG_ID, static::getSaveId()),
$this->namedtag->getTag(self::TAG_X),
$this->namedtag->getTag(self::TAG_Y),
$this->namedtag->getTag(self::TAG_Z)
new IntTag(self::TAG_X, $this->x),
new IntTag(self::TAG_Y, $this->y),
new IntTag(self::TAG_Z, $this->z)
]);
$this->addAdditionalSpawnData($nbt);
return $nbt;