mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-22 08:44:01 +00:00
Cleaned up tile NBT handling, use new CompoundTag API methods
This commit is contained in:
parent
0b1a9ba062
commit
20b86bdea8
@ -32,28 +32,29 @@ use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\Player;
|
||||
|
||||
class Bed extends Spawnable{
|
||||
const TAG_COLOR = "color";
|
||||
|
||||
public function __construct(Level $level, CompoundTag $nbt){
|
||||
if(!isset($nbt->color) or !($nbt->color instanceof ByteTag)){
|
||||
$nbt->color = new ByteTag("color", 14); //default to old red
|
||||
if(!($nbt->getTag(self::TAG_COLOR) instanceof ByteTag)){
|
||||
$nbt->setTag(new ByteTag(self::TAG_COLOR, 14)); //default to old red
|
||||
}
|
||||
parent::__construct($level, $nbt);
|
||||
}
|
||||
|
||||
public function getColor() : int{
|
||||
return $this->namedtag->color->getValue();
|
||||
return $this->namedtag->getByte(self::TAG_COLOR);
|
||||
}
|
||||
|
||||
public function setColor(int $color){
|
||||
$this->namedtag->color->setValue($color & 0x0f);
|
||||
$this->namedtag->setByte(self::TAG_COLOR, $color & 0x0f);
|
||||
$this->onChanged();
|
||||
}
|
||||
|
||||
public function addAdditionalSpawnData(CompoundTag $nbt) : void{
|
||||
$nbt->color = $this->namedtag->color;
|
||||
$nbt->setTag($this->namedtag->getTag(self::TAG_COLOR));
|
||||
}
|
||||
|
||||
protected static function createAdditionalNBT(CompoundTag $nbt, Vector3 $pos, ?int $face = null, ?Item $item = null, ?Player $player = null) : void{
|
||||
$nbt->color = new ByteTag("color", $item !== null ? $item->getDamage() : 14); //default red
|
||||
$nbt->setByte(self::TAG_COLOR, $item !== null ? $item->getDamage() : 14); //default red
|
||||
}
|
||||
}
|
@ -32,14 +32,16 @@ use pocketmine\level\Level;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\nbt\tag\ListTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\Player;
|
||||
|
||||
class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
use NameableTrait;
|
||||
|
||||
const TAG_PAIRX = "pairx";
|
||||
const TAG_PAIRZ = "pairz";
|
||||
const TAG_PAIR_LEAD = "pairlead";
|
||||
|
||||
/** @var ChestInventory */
|
||||
protected $inventory;
|
||||
/** @var DoubleChestInventory */
|
||||
@ -49,8 +51,8 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
parent::__construct($level, $nbt);
|
||||
$this->inventory = new ChestInventory($this);
|
||||
|
||||
if(!isset($this->namedtag->Items) or !($this->namedtag->Items instanceof ListTag)){
|
||||
$this->namedtag->Items = new ListTag("Items", [], NBT::TAG_Compound);
|
||||
if(!($this->namedtag->getTag("Items") instanceof ListTag)){
|
||||
$this->namedtag->setTag(new ListTag("Items", [], NBT::TAG_Compound));
|
||||
}
|
||||
|
||||
for($i = 0; $i < $this->getSize(); ++$i){
|
||||
@ -75,7 +77,7 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
}
|
||||
|
||||
public function saveNBT() : void{
|
||||
$this->namedtag->Items->setValue([]);
|
||||
$this->namedtag->setTag(new ListTag("Items", [], NBT::TAG_Compound));
|
||||
for($index = 0; $index < $this->getSize(); ++$index){
|
||||
$this->setItem($index, $this->inventory->getItem($index));
|
||||
}
|
||||
@ -94,8 +96,11 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
* @return int
|
||||
*/
|
||||
protected function getSlotIndex(int $index) : int{
|
||||
foreach($this->namedtag->Items as $i => $slot){
|
||||
if($slot->Slot->getValue() === $index){
|
||||
$items = $this->namedtag->getListTag("Items");
|
||||
assert($items instanceof ListTag);
|
||||
foreach($items as $i => $slot){
|
||||
/** @var CompoundTag $slot */
|
||||
if($slot->getByte("Slot") === $index){
|
||||
return (int) $i;
|
||||
}
|
||||
}
|
||||
@ -115,7 +120,7 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
if($i < 0){
|
||||
return ItemFactory::get(Item::AIR, 0, 0);
|
||||
}else{
|
||||
return Item::nbtDeserialize($this->namedtag->Items[$i]);
|
||||
return Item::nbtDeserialize($this->namedtag->getListTag("Items")[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,20 +135,25 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
|
||||
$d = $item->nbtSerialize($index);
|
||||
|
||||
$items = $this->namedtag->getListTag("Items");
|
||||
assert($items instanceof ListTag);
|
||||
|
||||
if($item->isNull()){
|
||||
if($i >= 0){
|
||||
unset($this->namedtag->Items[$i]);
|
||||
unset($items[$i]);
|
||||
}
|
||||
}elseif($i < 0){
|
||||
for($i = 0; $i <= $this->getSize(); ++$i){
|
||||
if(!isset($this->namedtag->Items[$i])){
|
||||
if(!isset($items[$i])){
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->namedtag->Items[$i] = $d;
|
||||
$items[$i] = $d;
|
||||
}else{
|
||||
$this->namedtag->Items[$i] = $d;
|
||||
$items[$i] = $d;
|
||||
}
|
||||
|
||||
$this->namedtag->setTag($items);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -164,7 +174,7 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
}
|
||||
|
||||
protected function checkPairing(){
|
||||
if($this->isPaired() and !$this->getLevel()->isChunkLoaded($this->namedtag->pairx->getValue() >> 4, $this->namedtag->pairz->getValue() >> 4)){
|
||||
if($this->isPaired() and !$this->getLevel()->isChunkLoaded($this->namedtag->getInt(self::TAG_PAIRX) >> 4, $this->namedtag->getInt(self::TAG_PAIRZ) >> 4)){
|
||||
//paired to a tile in an unloaded chunk
|
||||
$this->doubleInventory = null;
|
||||
|
||||
@ -182,7 +192,7 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
}
|
||||
}else{
|
||||
$this->doubleInventory = null;
|
||||
unset($this->namedtag->pairx, $this->namedtag->pairz);
|
||||
$this->namedtag->removeTag(self::TAG_PAIRX, self::TAG_PAIRZ);
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,7 +204,7 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
}
|
||||
|
||||
public function isPaired(){
|
||||
return isset($this->namedtag->pairx) and isset($this->namedtag->pairz);
|
||||
return $this->namedtag->hasTag(self::TAG_PAIRX) and $this->namedtag->hasTag(self::TAG_PAIRZ);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -202,7 +212,7 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
*/
|
||||
public function getPair() : ?Chest{
|
||||
if($this->isPaired()){
|
||||
$tile = $this->getLevel()->getTile(new Vector3($this->namedtag->pairx->getValue(), $this->y, $this->namedtag->pairz->getValue()));
|
||||
$tile = $this->getLevel()->getTile(new Vector3($this->namedtag->getInt(self::TAG_PAIRX), $this->y, $this->namedtag->getInt(self::TAG_PAIRZ)));
|
||||
if($tile instanceof Chest){
|
||||
return $tile;
|
||||
}
|
||||
@ -226,11 +236,11 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
}
|
||||
|
||||
private function createPair(Chest $tile){
|
||||
$this->namedtag->pairx = new IntTag("pairx", $tile->x);
|
||||
$this->namedtag->pairz = new IntTag("pairz", $tile->z);
|
||||
$this->namedtag->setInt(self::TAG_PAIRX, $tile->x);
|
||||
$this->namedtag->setInt(self::TAG_PAIRZ, $tile->z);
|
||||
|
||||
$tile->namedtag->pairx = new IntTag("pairx", $this->x);
|
||||
$tile->namedtag->pairz = new IntTag("pairz", $this->z);
|
||||
$tile->namedtag->setInt(self::TAG_PAIRX, $this->x);
|
||||
$tile->namedtag->setInt(self::TAG_PAIRZ, $this->z);
|
||||
}
|
||||
|
||||
public function unpair(){
|
||||
@ -239,12 +249,12 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
}
|
||||
|
||||
$tile = $this->getPair();
|
||||
unset($this->namedtag->pairx, $this->namedtag->pairz);
|
||||
$this->namedtag->removeTag(self::TAG_PAIRX, self::TAG_PAIRZ);
|
||||
|
||||
$this->spawnToAll();
|
||||
|
||||
if($tile instanceof Chest){
|
||||
unset($tile->namedtag->pairx, $tile->namedtag->pairz);
|
||||
$tile->namedtag->removeTag(self::TAG_PAIRX, self::TAG_PAIRZ);
|
||||
$tile->checkPairing();
|
||||
$tile->spawnToAll();
|
||||
}
|
||||
@ -255,20 +265,20 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
|
||||
public function addAdditionalSpawnData(CompoundTag $nbt) : void{
|
||||
if($this->isPaired()){
|
||||
$nbt->pairx = $this->namedtag->pairx;
|
||||
$nbt->pairz = $this->namedtag->pairz;
|
||||
$nbt->setTag($this->namedtag->getTag(self::TAG_PAIRX));
|
||||
$nbt->setTag($this->namedtag->getTag(self::TAG_PAIRZ));
|
||||
}
|
||||
|
||||
if($this->hasName()){
|
||||
$nbt->CustomName = $this->namedtag->CustomName;
|
||||
$nbt->setTag($this->namedtag->getTag("CustomName"));
|
||||
}
|
||||
}
|
||||
|
||||
protected static function createAdditionalNBT(CompoundTag $nbt, Vector3 $pos, ?int $face = null, ?Item $item = null, ?Player $player = null) : void{
|
||||
$nbt->Items = new ListTag("Items", [], NBT::TAG_Compound);
|
||||
$nbt->setTag(new ListTag("Items", [], NBT::TAG_Compound));
|
||||
|
||||
if($item !== null and $item->hasCustomName()){
|
||||
$nbt->CustomName = new StringTag("CustomName", $item->getCustomName());
|
||||
$nbt->setString("CustomName", $item->getCustomName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,6 @@ namespace pocketmine\tile;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\Player;
|
||||
|
||||
class EnchantTable extends Spawnable implements Nameable{
|
||||
@ -41,13 +40,13 @@ class EnchantTable extends Spawnable implements Nameable{
|
||||
|
||||
public function addAdditionalSpawnData(CompoundTag $nbt) : void{
|
||||
if($this->hasName()){
|
||||
$nbt->CustomName = $this->namedtag->CustomName;
|
||||
$nbt->setTag($this->namedtag->getTag("CustomName"));
|
||||
}
|
||||
}
|
||||
|
||||
protected static function createAdditionalNBT(CompoundTag $nbt, Vector3 $pos, ?int $face = null, ?Item $item = null, ?Player $player = null) : void{
|
||||
if($item !== null and $item->hasCustomName()){
|
||||
$nbt->CustomName = new StringTag("CustomName", $item->getCustomName());
|
||||
$nbt->setString("CustomName", $item->getCustomName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,13 +33,15 @@ use pocketmine\nbt\tag\ShortTag;
|
||||
use pocketmine\Player;
|
||||
|
||||
class FlowerPot extends Spawnable{
|
||||
const TAG_ITEM = "item";
|
||||
const TAG_ITEM_DATA = "mData";
|
||||
|
||||
public function __construct(Level $level, CompoundTag $nbt){
|
||||
if(!isset($nbt->item)){
|
||||
$nbt->item = new ShortTag("item", 0);
|
||||
if(!($nbt->getTag(self::TAG_ITEM) instanceof ShortTag)){
|
||||
$nbt->setTag(new ShortTag(self::TAG_ITEM, 0));
|
||||
}
|
||||
if(!isset($nbt->mData)){
|
||||
$nbt->mData = new IntTag("mData", 0);
|
||||
if(!($nbt->getTag(self::TAG_ITEM_DATA) instanceof IntTag)){
|
||||
$nbt->setTag(new IntTag(self::TAG_ITEM_DATA, 0));
|
||||
}
|
||||
parent::__construct($level, $nbt);
|
||||
}
|
||||
@ -68,12 +70,12 @@ class FlowerPot extends Spawnable{
|
||||
}
|
||||
|
||||
public function getItem() : Item{
|
||||
return ItemFactory::get($this->namedtag->item->getValue(), $this->namedtag->mData->getValue(), 1);
|
||||
return ItemFactory::get($this->namedtag->getShort(self::TAG_ITEM), $this->namedtag->getInt(self::TAG_ITEM_DATA), 1);
|
||||
}
|
||||
|
||||
public function setItem(Item $item){
|
||||
$this->namedtag->item->setValue($item->getId());
|
||||
$this->namedtag->mData->setValue($item->getDamage());
|
||||
$this->namedtag->setShort(self::TAG_ITEM, $item->getId());
|
||||
$this->namedtag->setInt(self::TAG_ITEM_DATA, $item->getDamage());
|
||||
$this->onChanged();
|
||||
}
|
||||
|
||||
@ -86,12 +88,12 @@ class FlowerPot extends Spawnable{
|
||||
}
|
||||
|
||||
public function addAdditionalSpawnData(CompoundTag $nbt) : void{
|
||||
$nbt->item = $this->namedtag->item;
|
||||
$nbt->mData = $this->namedtag->mData;
|
||||
$nbt->setTag($this->namedtag->getTag(self::TAG_ITEM));
|
||||
$nbt->setTag($this->namedtag->getTag(self::TAG_ITEM_DATA));
|
||||
}
|
||||
|
||||
protected static function createAdditionalNBT(CompoundTag $nbt, Vector3 $pos, ?int $face = null, ?Item $item = null, ?Player $player = null) : void{
|
||||
$nbt->item = new ShortTag("item", 0);
|
||||
$nbt->mData = new IntTag("mData", 0);
|
||||
$nbt->setShort(self::TAG_ITEM, 0);
|
||||
$nbt->setInt(self::TAG_ITEM_DATA, 0);
|
||||
}
|
||||
}
|
@ -38,44 +38,54 @@ use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\ListTag;
|
||||
use pocketmine\nbt\tag\ShortTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\network\mcpe\protocol\ContainerSetDataPacket;
|
||||
use pocketmine\Player;
|
||||
|
||||
class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
use NameableTrait;
|
||||
|
||||
const TAG_BURN_TIME = "BurnTime";
|
||||
const TAG_COOK_TIME = "CookTime";
|
||||
const TAG_MAX_TIME = "MaxTime";
|
||||
const TAG_BURN_TICKS = "BurnTicks";
|
||||
|
||||
/** @var FurnaceInventory */
|
||||
protected $inventory;
|
||||
|
||||
public function __construct(Level $level, CompoundTag $nbt){
|
||||
if(!isset($nbt->BurnTime) or $nbt->BurnTime->getValue() < 0){
|
||||
$nbt->BurnTime = new ShortTag("BurnTime", 0);
|
||||
}
|
||||
if(!isset($nbt->CookTime) or $nbt->CookTime->getValue() < 0 or ($nbt->BurnTime->getValue() === 0 and $nbt->CookTime->getValue() > 0)){
|
||||
$nbt->CookTime = new ShortTag("CookTime", 0);
|
||||
}
|
||||
if(!isset($nbt->MaxTime)){
|
||||
$nbt->MaxTime = new ShortTag("BurnTime", $nbt->BurnTime->getValue());
|
||||
unset($nbt->BurnTicks);
|
||||
if(!($nbt->getTag(self::TAG_BURN_TIME) instanceof ShortTag) or $nbt->getShort(self::TAG_BURN_TIME) < 0){
|
||||
$nbt->setTag(new ShortTag(self::TAG_BURN_TIME, 0));
|
||||
}
|
||||
|
||||
if(!isset($nbt->BurnTicks)){
|
||||
$nbt->BurnTicks = new ShortTag("BurnTicks", 0);
|
||||
if(!
|
||||
($nbt->getTag(self::TAG_COOK_TIME) instanceof ShortTag) or
|
||||
$nbt->getShort(self::TAG_COOK_TIME) < 0 or
|
||||
($nbt->getShort(self::TAG_BURN_TIME) === 0 and $nbt->getShort(self::TAG_COOK_TIME) > 0)
|
||||
){
|
||||
$nbt->setTag(new ShortTag(self::TAG_COOK_TIME, 0));
|
||||
}
|
||||
|
||||
if(!($nbt->getTag(self::TAG_MAX_TIME) instanceof ShortTag)){
|
||||
$nbt->setTag(new ShortTag(self::TAG_MAX_TIME, $nbt->getShort(self::TAG_BURN_TIME)));
|
||||
$nbt->removeTag(self::TAG_BURN_TICKS);
|
||||
}
|
||||
|
||||
if(!($nbt->getTag(self::TAG_BURN_TICKS) instanceof ShortTag)){
|
||||
$nbt->setTag(new ShortTag(self::TAG_BURN_TICKS, 0));
|
||||
}
|
||||
|
||||
parent::__construct($level, $nbt);
|
||||
$this->inventory = new FurnaceInventory($this);
|
||||
|
||||
if(!isset($this->namedtag->Items) or !($this->namedtag->Items instanceof ListTag)){
|
||||
$this->namedtag->Items = new ListTag("Items", [], NBT::TAG_Compound);
|
||||
if(!($this->namedtag->getTag("Items") instanceof ListTag)){
|
||||
$this->namedtag->setTag(new ListTag("Items", [], NBT::TAG_Compound));
|
||||
}
|
||||
|
||||
for($i = 0; $i < $this->getSize(); ++$i){
|
||||
$this->inventory->setItem($i, $this->getItem($i), false);
|
||||
}
|
||||
|
||||
if($this->namedtag->BurnTime->getValue() > 0){
|
||||
if($this->namedtag->getShort(self::TAG_BURN_TIME) > 0){
|
||||
$this->scheduleUpdate();
|
||||
}
|
||||
}
|
||||
@ -97,7 +107,7 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
}
|
||||
|
||||
public function saveNBT() : void{
|
||||
$this->namedtag->Items->setValue([]);
|
||||
$this->namedtag->setTag(new ListTag("Items", [], NBT::TAG_Compound));
|
||||
for($index = 0; $index < $this->getSize(); ++$index){
|
||||
$this->setItem($index, $this->inventory->getItem($index));
|
||||
}
|
||||
@ -116,8 +126,9 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
* @return int
|
||||
*/
|
||||
protected function getSlotIndex(int $index) : int{
|
||||
foreach($this->namedtag->Items as $i => $slot){
|
||||
if($slot->Slot->getValue() === $index){
|
||||
foreach($this->namedtag->getListTag("Items") as $i => $slot){
|
||||
/** @var CompoundTag $slot */
|
||||
if($slot->getByte("Slot") === $index){
|
||||
return (int) $i;
|
||||
}
|
||||
}
|
||||
@ -137,7 +148,7 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
if($i < 0){
|
||||
return ItemFactory::get(Item::AIR, 0, 0);
|
||||
}else{
|
||||
return Item::nbtDeserialize($this->namedtag->Items[$i]);
|
||||
return Item::nbtDeserialize($this->namedtag->getListTag("Items")[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,20 +163,25 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
|
||||
$d = $item->nbtSerialize($index);
|
||||
|
||||
$items = $this->namedtag->getListTag("Items");
|
||||
assert($items instanceof ListTag);
|
||||
|
||||
if($item->isNull()){
|
||||
if($i >= 0){
|
||||
unset($this->namedtag->Items[$i]);
|
||||
unset($items[$i]);
|
||||
}
|
||||
}elseif($i < 0){
|
||||
for($i = 0; $i <= $this->getSize(); ++$i){
|
||||
if(!isset($this->namedtag->Items[$i])){
|
||||
if(!isset($items[$i])){
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->namedtag->Items[$i] = $d;
|
||||
$items[$i] = $d;
|
||||
}else{
|
||||
$this->namedtag->Items[$i] = $d;
|
||||
$items[$i] = $d;
|
||||
}
|
||||
|
||||
$this->namedtag->setTag($items);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -182,14 +198,14 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->namedtag->MaxTime->setValue($ev->getBurnTime());
|
||||
$this->namedtag->BurnTime->setValue($ev->getBurnTime());
|
||||
$this->namedtag->BurnTicks->setValue(0);
|
||||
$this->namedtag->setShort(self::TAG_MAX_TIME, $ev->getBurnTime());
|
||||
$this->namedtag->setShort(self::TAG_BURN_TIME, $ev->getBurnTime());
|
||||
$this->namedtag->setShort(self::TAG_BURN_TICKS, 0);
|
||||
if($this->getBlock()->getId() === Block::FURNACE){
|
||||
$this->getLevel()->setBlock($this, BlockFactory::get(Block::BURNING_FURNACE, $this->getBlock()->getDamage()), true);
|
||||
}
|
||||
|
||||
if($this->namedtag->BurnTime->getValue() > 0 and $ev->isBurning()){
|
||||
if($this->namedtag->getShort(self::TAG_BURN_TIME) > 0 and $ev->isBurning()){
|
||||
$fuel->pop();
|
||||
$this->inventory->setFuel($fuel);
|
||||
}
|
||||
@ -210,17 +226,17 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
$smelt = $this->server->getCraftingManager()->matchFurnaceRecipe($raw);
|
||||
$canSmelt = ($smelt instanceof FurnaceRecipe and $raw->getCount() > 0 and (($smelt->getResult()->equals($product) and $product->getCount() < $product->getMaxStackSize()) or $product->isNull()));
|
||||
|
||||
if($this->namedtag->BurnTime->getValue() <= 0 and $canSmelt and $fuel->getFuelTime() > 0 and $fuel->getCount() > 0){
|
||||
if($this->namedtag->getShort(self::TAG_BURN_TIME) <= 0 and $canSmelt and $fuel->getFuelTime() > 0 and $fuel->getCount() > 0){
|
||||
$this->checkFuel($fuel);
|
||||
}
|
||||
|
||||
if($this->namedtag->BurnTime->getValue() > 0){
|
||||
$this->namedtag->BurnTime->setValue($this->namedtag->BurnTime->getValue() - 1);
|
||||
$this->namedtag->BurnTicks->setValue((int) ceil($this->namedtag->BurnTime->getValue() / $this->namedtag->MaxTime->getValue() * 200));
|
||||
if($this->namedtag->getShort(self::TAG_BURN_TIME) > 0){
|
||||
$this->namedtag->setShort(self::TAG_BURN_TIME, $this->namedtag->getShort(self::TAG_BURN_TIME) - 1);
|
||||
$this->namedtag->setShort(self::TAG_BURN_TICKS, (int) ceil($this->namedtag->getShort(self::TAG_BURN_TIME) / $this->namedtag->getShort(self::TAG_MAX_TIME) * 200));
|
||||
|
||||
if($smelt instanceof FurnaceRecipe and $canSmelt){
|
||||
$this->namedtag->CookTime->setValue($this->namedtag->CookTime->getValue() + 1);
|
||||
if($this->namedtag->CookTime->getValue() >= 200){ //10 seconds
|
||||
$this->namedtag->setShort(self::TAG_COOK_TIME, $this->namedtag->getShort(self::TAG_COOK_TIME) + 1);
|
||||
if($this->namedtag->getShort(self::TAG_COOK_TIME) >= 200){ //10 seconds
|
||||
$product = ItemFactory::get($smelt->getResult()->getId(), $smelt->getResult()->getDamage(), $product->getCount() + 1);
|
||||
|
||||
$this->server->getPluginManager()->callEvent($ev = new FurnaceSmeltEvent($this, $raw, $product));
|
||||
@ -231,23 +247,23 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
$this->inventory->setSmelting($raw);
|
||||
}
|
||||
|
||||
$this->namedtag->CookTime->setValue($this->namedtag->CookTime->getValue() - 200);
|
||||
$this->namedtag->setShort(self::TAG_COOK_TIME, $this->namedtag->getShort(self::TAG_COOK_TIME) - 200);
|
||||
}
|
||||
}elseif($this->namedtag->BurnTime->getValue() <= 0){
|
||||
$this->namedtag->BurnTime->setValue(0);
|
||||
$this->namedtag->CookTime->setValue(0);
|
||||
$this->namedtag->BurnTicks->setValue(0);
|
||||
}elseif($this->namedtag->getShort(self::TAG_BURN_TIME) <= 0){
|
||||
$this->namedtag->setShort(self::TAG_BURN_TIME, 0);
|
||||
$this->namedtag->setShort(self::TAG_COOK_TIME, 0);
|
||||
$this->namedtag->setShort(self::TAG_BURN_TICKS, 0);
|
||||
}else{
|
||||
$this->namedtag->CookTime->setValue(0);
|
||||
$this->namedtag->setShort(self::TAG_COOK_TIME, 0);
|
||||
}
|
||||
$ret = true;
|
||||
}else{
|
||||
if($this->getBlock()->getId() === Block::BURNING_FURNACE){
|
||||
$this->getLevel()->setBlock($this, BlockFactory::get(Block::FURNACE, $this->getBlock()->getDamage()), true);
|
||||
}
|
||||
$this->namedtag->BurnTime->setValue(0);
|
||||
$this->namedtag->CookTime->setValue(0);
|
||||
$this->namedtag->BurnTicks->setValue(0);
|
||||
$this->namedtag->setShort(self::TAG_BURN_TIME, 0);
|
||||
$this->namedtag->setShort(self::TAG_COOK_TIME, 0);
|
||||
$this->namedtag->setShort(self::TAG_BURN_TICKS, 0);
|
||||
}
|
||||
|
||||
foreach($this->getInventory()->getViewers() as $player){
|
||||
@ -256,16 +272,15 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
$pk = new ContainerSetDataPacket();
|
||||
$pk->windowId = $windowId;
|
||||
$pk->property = ContainerSetDataPacket::PROPERTY_FURNACE_TICK_COUNT; //Smelting
|
||||
$pk->value = $this->namedtag->CookTime->getValue();
|
||||
$pk->value = $this->namedtag->getShort(self::TAG_COOK_TIME);
|
||||
$player->dataPacket($pk);
|
||||
|
||||
$pk = new ContainerSetDataPacket();
|
||||
$pk->windowId = $windowId;
|
||||
$pk->property = ContainerSetDataPacket::PROPERTY_FURNACE_LIT_TIME;
|
||||
$pk->value = $this->namedtag->BurnTicks->getValue();
|
||||
$pk->value = $this->namedtag->getShort(self::TAG_BURN_TICKS);
|
||||
$player->dataPacket($pk);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$this->timings->stopTiming();
|
||||
@ -274,19 +289,19 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
}
|
||||
|
||||
public function addAdditionalSpawnData(CompoundTag $nbt) : void{
|
||||
$nbt->BurnTime = $this->namedtag->BurnTime;
|
||||
$nbt->CookTime = $this->namedtag->CookTime;
|
||||
$nbt->setTag($this->namedtag->getTag(self::TAG_BURN_TIME));
|
||||
$nbt->setTag($this->namedtag->getTag(self::TAG_COOK_TIME));
|
||||
|
||||
if($this->hasName()){
|
||||
$nbt->CustomName = $this->namedtag->CustomName;
|
||||
$nbt->setTag($this->namedtag->getTag("CustomName"));
|
||||
}
|
||||
}
|
||||
|
||||
protected static function createAdditionalNBT(CompoundTag $nbt, Vector3 $pos, ?int $face = null, ?Item $item = null, ?Player $player = null) : void{
|
||||
$nbt->Items = new ListTag("Items", [], NBT::TAG_Compound);
|
||||
$nbt->setTag(new ListTag("Items", [], NBT::TAG_Compound));
|
||||
|
||||
if($item !== null and $item->hasCustomName()){
|
||||
$nbt->CustomName = new StringTag("CustomName", $item->getCustomName());
|
||||
$nbt->setString("CustomName", $item->getCustomName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,14 +33,17 @@ use pocketmine\nbt\tag\FloatTag;
|
||||
use pocketmine\Player;
|
||||
|
||||
class ItemFrame extends Spawnable{
|
||||
const TAG_ITEM_ROTATION = "ItemRotation";
|
||||
const TAG_ITEM_DROP_CHANCE = "ItemDropChance";
|
||||
const TAG_ITEM = "Item";
|
||||
|
||||
public function __construct(Level $level, CompoundTag $nbt){
|
||||
if(!isset($nbt->ItemRotation)){
|
||||
$nbt->ItemRotation = new ByteTag("ItemRotation", 0);
|
||||
if(!($nbt->getTag(self::TAG_ITEM_ROTATION) instanceof ByteTag)){
|
||||
$nbt->setTag(new ByteTag(self::TAG_ITEM_ROTATION, 0));
|
||||
}
|
||||
|
||||
if(!isset($nbt->ItemDropChance)){
|
||||
$nbt->ItemDropChance = new FloatTag("ItemDropChance", 1.0);
|
||||
if(!($nbt->getTag(self::TAG_ITEM_DROP_CHANCE) instanceof FloatTag)){
|
||||
$nbt->setTag(new FloatTag(self::TAG_ITEM_DROP_CHANCE, 1.0));
|
||||
}
|
||||
|
||||
parent::__construct($level, $nbt);
|
||||
@ -51,52 +54,52 @@ class ItemFrame extends Spawnable{
|
||||
}
|
||||
|
||||
public function getItem() : Item{
|
||||
if(isset($this->namedtag->Item)){
|
||||
return Item::nbtDeserialize($this->namedtag->Item);
|
||||
}else{
|
||||
return ItemFactory::get(Item::AIR, 0, 0);
|
||||
$c = $this->namedtag->getCompoundTag(self::TAG_ITEM);
|
||||
if($c !== null){
|
||||
return Item::nbtDeserialize($c);
|
||||
}
|
||||
|
||||
return ItemFactory::get(Item::AIR, 0, 0);
|
||||
}
|
||||
|
||||
public function setItem(Item $item = null){
|
||||
if($item !== null and !$item->isNull()){
|
||||
$this->namedtag->Item = $item->nbtSerialize(-1, "Item");
|
||||
$this->namedtag->setTag($item->nbtSerialize(-1, self::TAG_ITEM));
|
||||
}else{
|
||||
unset($this->namedtag->Item);
|
||||
$this->namedtag->removeTag(self::TAG_ITEM);
|
||||
}
|
||||
$this->onChanged();
|
||||
}
|
||||
|
||||
public function getItemRotation() : int{
|
||||
return $this->namedtag->ItemRotation->getValue();
|
||||
return $this->namedtag->getByte(self::TAG_ITEM_ROTATION);
|
||||
}
|
||||
|
||||
public function setItemRotation(int $rotation){
|
||||
$this->namedtag->ItemRotation->setValue($rotation);
|
||||
$this->namedtag->setByte(self::TAG_ITEM_ROTATION, $rotation);
|
||||
$this->onChanged();
|
||||
}
|
||||
|
||||
public function getItemDropChance() : float{
|
||||
return $this->namedtag->ItemDropChance->getValue();
|
||||
return $this->namedtag->getFloat(self::TAG_ITEM_DROP_CHANCE);
|
||||
}
|
||||
|
||||
public function setItemDropChance(float $chance){
|
||||
$this->namedtag->ItemDropChance->setValue($chance);
|
||||
$this->namedtag->setFloat(self::TAG_ITEM_DROP_CHANCE, $chance);
|
||||
$this->onChanged();
|
||||
}
|
||||
|
||||
public function addAdditionalSpawnData(CompoundTag $nbt) : void{
|
||||
$nbt->ItemDropChance = $this->namedtag->ItemDropChance;
|
||||
$nbt->ItemRotation = $this->namedtag->ItemRotation;
|
||||
$nbt->setTag($this->namedtag->getTag(self::TAG_ITEM_DROP_CHANCE));
|
||||
$nbt->setTag($this->namedtag->getTag(self::TAG_ITEM_ROTATION));
|
||||
|
||||
if($this->hasItem()){
|
||||
$nbt->Item = $this->namedtag->Item;
|
||||
$nbt->setTag($this->namedtag->getTag(self::TAG_ITEM));
|
||||
}
|
||||
}
|
||||
|
||||
protected static function createAdditionalNBT(CompoundTag $nbt, Vector3 $pos, ?int $face = null, ?Item $item = null, ?Player $player = null) : void{
|
||||
$nbt->ItemDropChance = new FloatTag("ItemDropChance", 1.0);
|
||||
$nbt->ItemRotation = new ByteTag("ItemRotation", 0);
|
||||
$nbt->setFloat(self::TAG_ITEM_DROP_CHANCE, 1.0);
|
||||
$nbt->setByte(self::TAG_ITEM_ROTATION, 0);
|
||||
}
|
||||
|
||||
}
|
@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
namespace pocketmine\tile;
|
||||
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
|
||||
/**
|
||||
* This trait implements most methods in the {@link Nameable} interface. It should only be used by Tiles.
|
||||
@ -46,7 +45,7 @@ trait NameableTrait{
|
||||
*/
|
||||
public function getName() : string{
|
||||
$nbt = $this->getNBT();
|
||||
return isset($nbt->CustomName) ? ((string) $nbt->CustomName->getValue()) : $this->getDefaultName();
|
||||
return $nbt->getString("CustomName") ?? $this->getDefaultName();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,17 +54,17 @@ trait NameableTrait{
|
||||
public function setName(string $name) : void{
|
||||
$nbt = $this->getNBT();
|
||||
if($name === ""){
|
||||
unset($nbt->CustomName);
|
||||
$nbt->removeTag("CustomName");
|
||||
return;
|
||||
}
|
||||
|
||||
$nbt->CustomName = new StringTag("CustomName", $name);
|
||||
$nbt->setString("CustomName", $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasName() : bool{
|
||||
return isset($this->getNBT()->CustomName);
|
||||
return $this->getNBT()->hasTag("CustomName");
|
||||
}
|
||||
}
|
@ -33,20 +33,24 @@ use pocketmine\Player;
|
||||
use pocketmine\utils\TextFormat;
|
||||
|
||||
class Sign extends Spawnable{
|
||||
const TAG_TEXT_BLOB = "Text";
|
||||
const TAG_TEXT_LINE = "Text%d"; //sprintf()able
|
||||
const TAG_CREATOR = "Creator";
|
||||
|
||||
/** @var string[] */
|
||||
protected $text = ["", "", "", ""];
|
||||
|
||||
public function __construct(Level $level, CompoundTag $nbt){
|
||||
if(isset($nbt->Text)){ //MCPE 1.2 save format
|
||||
$this->text = explode("\n", $nbt->Text->getValue());
|
||||
unset($nbt->Text);
|
||||
if($nbt->getTag(self::TAG_TEXT_BLOB) instanceof StringTag){ //MCPE 1.2 save format
|
||||
$this->text = explode("\n", $nbt->getString(self::TAG_TEXT_BLOB));
|
||||
assert(count($this->text) === 4, "Too many lines!");
|
||||
$nbt->removeTag(self::TAG_TEXT_BLOB);
|
||||
}else{
|
||||
for($i = 1; $i <= 4; ++$i){
|
||||
$textKey = "Text$i";
|
||||
if(isset($nbt->$textKey)){
|
||||
$this->text[$i - 1] = $nbt->$textKey->getValue();
|
||||
unset($nbt->$textKey);
|
||||
$textKey = sprintf(self::TAG_TEXT_LINE, $i);
|
||||
if($nbt->getTag($textKey) instanceof StringTag){
|
||||
$this->text[$i - 1] = $nbt->getString($textKey);
|
||||
$nbt->removeTag($textKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -56,14 +60,14 @@ class Sign extends Spawnable{
|
||||
|
||||
public function saveNBT() : void{
|
||||
parent::saveNBT();
|
||||
$this->namedtag->Text = new StringTag("Text", implode("\n", $this->text));
|
||||
$this->namedtag->setString(self::TAG_TEXT_BLOB, implode("\n", $this->text));
|
||||
|
||||
for($i = 1; $i <= 4; ++$i){ //Backwards-compatibility
|
||||
$textKey = "Text$i";
|
||||
$this->namedtag->$textKey = new StringTag($textKey, $this->getLine($i - 1));
|
||||
$textKey = sprintf(self::TAG_TEXT_LINE, $i);
|
||||
$this->namedtag->setString($textKey, $this->getLine($i - 1));
|
||||
}
|
||||
|
||||
unset($this->namedtag->Creator);
|
||||
$this->namedtag->removeTag(self::TAG_CREATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -128,22 +132,22 @@ class Sign extends Spawnable{
|
||||
}
|
||||
|
||||
public function addAdditionalSpawnData(CompoundTag $nbt) : void{
|
||||
$nbt->Text = new StringTag("Text", implode("\n", $this->text));
|
||||
$nbt->setString(self::TAG_TEXT_BLOB, implode("\n", $this->text));
|
||||
}
|
||||
|
||||
public function updateCompoundTag(CompoundTag $nbt, Player $player) : bool{
|
||||
if($nbt["id"] !== Tile::SIGN){
|
||||
if($nbt->getString("id") !== Tile::SIGN){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(isset($nbt->Text)){
|
||||
$lines = array_pad(explode("\n", $nbt->Text->getValue()), 4, "");
|
||||
if(($blob = $nbt->getString(self::TAG_TEXT_BLOB)) !== null){
|
||||
$lines = array_pad(explode("\n", $blob), 4, "");
|
||||
}else{
|
||||
$lines = [
|
||||
$nbt->Text1->getValue(),
|
||||
$nbt->Text2->getValue(),
|
||||
$nbt->Text3->getValue(),
|
||||
$nbt->Text4->getValue()
|
||||
$nbt->getString(sprintf(self::TAG_TEXT_LINE, 1)),
|
||||
$nbt->getString(sprintf(self::TAG_TEXT_LINE, 2)),
|
||||
$nbt->getString(sprintf(self::TAG_TEXT_LINE, 3)),
|
||||
$nbt->getString(sprintf(self::TAG_TEXT_LINE, 4))
|
||||
];
|
||||
}
|
||||
|
||||
@ -151,7 +155,7 @@ class Sign extends Spawnable{
|
||||
|
||||
$ev = new SignChangeEvent($this->getBlock(), $player, array_map(function(string $line) use ($removeFormat){ return TextFormat::clean($line, $removeFormat); }, $lines));
|
||||
|
||||
if(!isset($this->namedtag->Creator) or $this->namedtag->Creator->getValue() !== $player->getRawUniqueId()){
|
||||
if(($creatorUUID = $this->namedtag->getString(self::TAG_CREATOR)) !== null and $creatorUUID !== $player->getRawUniqueId()){
|
||||
$ev->setCancelled();
|
||||
}
|
||||
|
||||
@ -168,12 +172,11 @@ class Sign extends Spawnable{
|
||||
|
||||
protected static function createAdditionalNBT(CompoundTag $nbt, Vector3 $pos, ?int $face = null, ?Item $item = null, ?Player $player = null) : void{
|
||||
for($i = 1; $i <= 4; ++$i){
|
||||
$key = "Text$i";
|
||||
$nbt->$key = new StringTag($key, "");
|
||||
$nbt->setString(sprintf(self::TAG_TEXT_LINE, $i), "");
|
||||
}
|
||||
|
||||
if($player !== null){
|
||||
$nbt->Creator = new StringTag("Creator", $player->getRawUniqueId());
|
||||
$nbt->setString(self::TAG_CREATOR, $player->getRawUniqueId());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,37 +38,40 @@ class Skull extends Spawnable{
|
||||
const TYPE_CREEPER = 4;
|
||||
const TYPE_DRAGON = 5;
|
||||
|
||||
const TAG_SKULL_TYPE = "SkullType";
|
||||
const TAG_ROT = "Rot";
|
||||
|
||||
public function __construct(Level $level, CompoundTag $nbt){
|
||||
if(!isset($nbt->SkullType)){
|
||||
$nbt->SkullType = new ByteTag("SkullType", 0);
|
||||
if(!($nbt->getTag(self::TAG_SKULL_TYPE) instanceof ByteTag)){
|
||||
$nbt->setTag(new ByteTag(self::TAG_SKULL_TYPE, 0));
|
||||
}
|
||||
if(!isset($nbt->Rot)){
|
||||
$nbt->Rot = new ByteTag("Rot", 0);
|
||||
if(!($nbt->getTag(self::TAG_ROT) instanceof ByteTag)){
|
||||
$nbt->setTag(new ByteTag(self::TAG_ROT, 0));
|
||||
}
|
||||
parent::__construct($level, $nbt);
|
||||
}
|
||||
|
||||
public function setType(int $type){
|
||||
$this->namedtag->SkullType->setValue($type);
|
||||
$this->namedtag->setByte(self::TAG_SKULL_TYPE, $type);
|
||||
$this->onChanged();
|
||||
}
|
||||
|
||||
public function getType() : int{
|
||||
return $this->namedtag->SkullType->getValue();
|
||||
return $this->namedtag->getByte(self::TAG_SKULL_TYPE);
|
||||
}
|
||||
|
||||
public function addAdditionalSpawnData(CompoundTag $nbt) : void{
|
||||
$nbt->SkullType = $this->namedtag->SkullType;
|
||||
$nbt->Rot = $this->namedtag->Rot;
|
||||
$nbt->setTag($this->namedtag->getTag(self::TAG_SKULL_TYPE));
|
||||
$nbt->setTag($this->namedtag->getTag(self::TAG_ROT));
|
||||
}
|
||||
|
||||
protected static function createAdditionalNBT(CompoundTag $nbt, Vector3 $pos, ?int $face = null, ?Item $item = null, ?Player $player = null) : void{
|
||||
$nbt->SkullType = new ByteTag("SkullType", $item !== null ? $item->getDamage() : self::TYPE_SKELETON);
|
||||
$nbt->setByte(self::TAG_SKULL_TYPE, $item !== null ? $item->getDamage() : self::TYPE_SKELETON);
|
||||
|
||||
$rot = 0;
|
||||
if($face === Vector3::SIDE_UP and $player !== null){
|
||||
$rot = floor(($player->yaw * 16 / 360) + 0.5) & 0x0F;
|
||||
}
|
||||
$nbt->Rot = new ByteTag("Rot", $rot);
|
||||
$nbt->setByte("Rot", $rot);
|
||||
}
|
||||
}
|
@ -81,10 +81,10 @@ abstract class Spawnable extends Tile{
|
||||
*/
|
||||
final public function getSpawnCompound() : CompoundTag{
|
||||
$nbt = new CompoundTag("", [
|
||||
$this->namedtag->id,
|
||||
$this->namedtag->x,
|
||||
$this->namedtag->y,
|
||||
$this->namedtag->z
|
||||
$this->namedtag->getTag("id"),
|
||||
$this->namedtag->getTag("x"),
|
||||
$this->namedtag->getTag("y"),
|
||||
$this->namedtag->getTag("z")
|
||||
]);
|
||||
$this->addAdditionalSpawnData($nbt);
|
||||
return $nbt;
|
||||
|
@ -136,14 +136,14 @@ abstract class Tile extends Position{
|
||||
$this->namedtag = $nbt;
|
||||
$this->server = $level->getServer();
|
||||
$this->setLevel($level);
|
||||
$this->chunk = $level->getChunk($this->namedtag->x->getValue() >> 4, $this->namedtag->z->getValue() >> 4, false);
|
||||
$this->chunk = $level->getChunk($this->namedtag->getInt("x") >> 4, $this->namedtag->getInt("z") >> 4, false);
|
||||
assert($this->chunk !== null);
|
||||
|
||||
$this->name = "";
|
||||
$this->id = Tile::$tileCount++;
|
||||
$this->x = $this->namedtag->x->getValue();
|
||||
$this->y = $this->namedtag->y->getValue();
|
||||
$this->z = $this->namedtag->z->getValue();
|
||||
$this->x = $this->namedtag->getInt("x");
|
||||
$this->y = $this->namedtag->getInt("y");
|
||||
$this->z = $this->namedtag->getInt("z");
|
||||
|
||||
$this->chunk->addTile($this);
|
||||
$this->getLevel()->addTile($this);
|
||||
@ -154,10 +154,10 @@ abstract class Tile extends Position{
|
||||
}
|
||||
|
||||
public function saveNBT() : void{
|
||||
$this->namedtag->id->setValue(static::getSaveId());
|
||||
$this->namedtag->x->setValue($this->x);
|
||||
$this->namedtag->y->setValue($this->y);
|
||||
$this->namedtag->z->setValue($this->z);
|
||||
$this->namedtag->setString("id", static::getSaveId());
|
||||
$this->namedtag->setInt("x", $this->x);
|
||||
$this->namedtag->setInt("y", $this->y);
|
||||
$this->namedtag->setInt("z", $this->z);
|
||||
}
|
||||
|
||||
public function getNBT() : CompoundTag{
|
||||
@ -167,7 +167,7 @@ abstract class Tile extends Position{
|
||||
public function getCleanedNBT() : ?CompoundTag{
|
||||
$this->saveNBT();
|
||||
$tag = clone $this->namedtag;
|
||||
unset($tag->x, $tag->y, $tag->z, $tag->id);
|
||||
$tag->removeTag("x", "y", "z", "id");
|
||||
if($tag->getCount() > 0){
|
||||
return $tag;
|
||||
}else{
|
||||
|
Loading…
x
Reference in New Issue
Block a user