Removed pocketmine subdirectory, map PSR-4 style

This commit is contained in:
Dylan K. Taylor
2019-07-30 19:14:57 +01:00
parent 7a77d3dc30
commit 5499ac620c
1044 changed files with 3 additions and 3 deletions

131
src/block/tile/Banner.php Normal file
View File

@ -0,0 +1,131 @@
<?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\block\tile;
use Ds\Deque;
use pocketmine\block\utils\BannerPattern;
use pocketmine\block\utils\DyeColor;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\world\World;
/**
* @deprecated
* @see \pocketmine\block\Banner
*/
class Banner extends Spawnable{
public const TAG_BASE = "Base";
public const TAG_PATTERNS = "Patterns";
public const TAG_PATTERN_COLOR = "Color";
public const TAG_PATTERN_NAME = "Pattern";
/** @var DyeColor */
private $baseColor;
/** @var BannerPattern[]|Deque */
private $patterns = [];
public function __construct(World $world, Vector3 $pos){
$this->baseColor = DyeColor::BLACK();
$this->patterns = new Deque();
parent::__construct($world, $pos);
}
public function readSaveData(CompoundTag $nbt) : void{
if($nbt->hasTag(self::TAG_BASE, IntTag::class)){
$this->baseColor = DyeColor::fromMagicNumber($nbt->getInt(self::TAG_BASE), true);
}
$patterns = $nbt->getListTag(self::TAG_PATTERNS);
if($patterns !== null){
/** @var CompoundTag $pattern */
foreach($patterns as $pattern){
$this->patterns[] = new BannerPattern($pattern->getString(self::TAG_PATTERN_NAME), DyeColor::fromMagicNumber($pattern->getInt(self::TAG_PATTERN_COLOR), true));
}
}
}
protected function writeSaveData(CompoundTag $nbt) : void{
$nbt->setInt(self::TAG_BASE, $this->baseColor->getInvertedMagicNumber());
$patterns = new ListTag();
foreach($this->patterns as $pattern){
$patterns->push(CompoundTag::create()
->setString(self::TAG_PATTERN_NAME, $pattern->getId())
->setInt(self::TAG_PATTERN_COLOR, $pattern->getColor()->getInvertedMagicNumber())
);
}
$nbt->setTag(self::TAG_PATTERNS, $patterns);
}
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
$nbt->setInt(self::TAG_BASE, $this->baseColor->getInvertedMagicNumber());
$patterns = new ListTag();
foreach($this->patterns as $pattern){
$patterns->push(CompoundTag::create()
->setString(self::TAG_PATTERN_NAME, $pattern->getId())
->setInt(self::TAG_PATTERN_COLOR, $pattern->getColor()->getInvertedMagicNumber())
);
}
$nbt->setTag(self::TAG_PATTERNS, $patterns);
}
/**
* Returns the color of the banner base.
*
* @return DyeColor
*/
public function getBaseColor() : DyeColor{
return $this->baseColor;
}
/**
* Sets the color of the banner base.
*
* @param DyeColor $color
*/
public function setBaseColor(DyeColor $color) : void{
$this->baseColor = $color;
}
/**
* @return BannerPattern[]|Deque
*/
public function getPatterns() : Deque{
return $this->patterns;
}
/**
* @param BannerPattern[]|Deque $patterns
*/
public function setPatterns(Deque $patterns) : void{
$this->patterns = $patterns;
}
public function getDefaultName() : string{
return "Banner";
}
}

63
src/block/tile/Bed.php Normal file
View File

@ -0,0 +1,63 @@
<?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\block\tile;
use pocketmine\block\utils\DyeColor;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\world\World;
class Bed extends Spawnable{
public const TAG_COLOR = "color";
/** @var DyeColor */
private $color;
public function __construct(World $world, Vector3 $pos){
$this->color = DyeColor::RED();
parent::__construct($world, $pos);
}
public function getColor() : DyeColor{
return $this->color;
}
public function setColor(DyeColor $color) : void{
$this->color = $color;
}
public function readSaveData(CompoundTag $nbt) : void{
if($nbt->hasTag(self::TAG_COLOR, ByteTag::class)){
$this->color = DyeColor::fromMagicNumber($nbt->getByte(self::TAG_COLOR));
}
}
protected function writeSaveData(CompoundTag $nbt) : void{
$nbt->setByte(self::TAG_COLOR, $this->color->getMagicNumber());
}
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
$nbt->setByte(self::TAG_COLOR, $this->color->getMagicNumber());
}
}

View File

@ -0,0 +1,113 @@
<?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\block\tile;
use pocketmine\inventory\BrewingStandInventory;
use pocketmine\inventory\CallbackInventoryChangeListener;
use pocketmine\inventory\Inventory;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\world\World;
class BrewingStand extends Spawnable implements Container, Nameable{
use ContainerTrait;
use NameableTrait;
private const TAG_BREW_TIME = "BrewTime"; //TAG_Short
private const TAG_BREW_TIME_PE = "CookTime"; //TAG_Short
private const TAG_MAX_FUEL_TIME = "FuelTotal"; //TAG_Short
private const TAG_REMAINING_FUEL_TIME = "Fuel"; //TAG_Byte
private const TAG_REMAINING_FUEL_TIME_PE = "FuelAmount"; //TAG_Short
/** @var BrewingStandInventory */
private $inventory;
/** @var int */
private $brewTime = 0;
/** @var int */
private $maxFuelTime = 0;
/** @var int */
private $remainingFuelTime = 0;
public function __construct(World $world, Vector3 $pos){
$this->inventory = new BrewingStandInventory($this);
$this->inventory->addChangeListeners(CallbackInventoryChangeListener::onAnyChange(function(Inventory $unused){
$this->world->scheduleDelayedBlockUpdate($this->getBlock(), 1);
}));
parent::__construct($world, $pos);
}
public function readSaveData(CompoundTag $nbt) : void{
$this->loadName($nbt);
$this->loadItems($nbt);
$this->brewTime = $nbt->getShort(self::TAG_BREW_TIME, $nbt->getShort(self::TAG_BREW_TIME_PE, 0));
$this->maxFuelTime = $nbt->getShort(self::TAG_MAX_FUEL_TIME, 0);
$this->remainingFuelTime = $nbt->getByte(self::TAG_REMAINING_FUEL_TIME, $nbt->getShort(self::TAG_REMAINING_FUEL_TIME_PE, 0));
if($this->maxFuelTime === 0){
$this->maxFuelTime = $this->remainingFuelTime;
}
if($this->remainingFuelTime === 0){
$this->maxFuelTime = $this->remainingFuelTime = $this->brewTime = 0;
}
}
protected function writeSaveData(CompoundTag $nbt) : void{
$this->saveName($nbt);
$this->saveItems($nbt);
$nbt->setShort(self::TAG_BREW_TIME_PE, $this->brewTime);
$nbt->setShort(self::TAG_MAX_FUEL_TIME, $this->maxFuelTime);
$nbt->setShort(self::TAG_REMAINING_FUEL_TIME_PE, $this->remainingFuelTime);
}
public function getDefaultName() : string{
return "Brewing Stand";
}
public function close() : void{
if(!$this->closed){
$this->inventory->removeAllViewers();
$this->inventory = null;
parent::close();
}
}
/**
* @return BrewingStandInventory
*/
public function getInventory(){
return $this->inventory;
}
/**
* @return BrewingStandInventory
*/
public function getRealInventory(){
return $this->inventory;
}
}

242
src/block/tile/Chest.php Normal file
View File

@ -0,0 +1,242 @@
<?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\block\tile;
use pocketmine\inventory\ChestInventory;
use pocketmine\inventory\DoubleChestInventory;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\world\World;
use function abs;
class Chest extends Spawnable implements Container, Nameable{
use NameableTrait {
addAdditionalSpawnData as addNameSpawnData;
}
use ContainerTrait {
onBlockDestroyedHook as containerTraitBlockDestroyedHook;
}
public const TAG_PAIRX = "pairx";
public const TAG_PAIRZ = "pairz";
public const TAG_PAIR_LEAD = "pairlead";
/** @var ChestInventory */
protected $inventory;
/** @var DoubleChestInventory */
protected $doubleInventory = null;
/** @var int|null */
private $pairX;
/** @var int|null */
private $pairZ;
public function __construct(World $world, Vector3 $pos){
$this->inventory = new ChestInventory($this);
parent::__construct($world, $pos);
}
public function readSaveData(CompoundTag $nbt) : void{
if($nbt->hasTag(self::TAG_PAIRX, IntTag::class) and $nbt->hasTag(self::TAG_PAIRZ, IntTag::class)){
$pairX = $nbt->getInt(self::TAG_PAIRX);
$pairZ = $nbt->getInt(self::TAG_PAIRZ);
if(
($this->x === $pairX and abs($this->z - $pairZ) === 1) or
($this->z === $pairZ and abs($this->x - $pairX) === 1)
){
$this->pairX = $pairX;
$this->pairZ = $pairZ;
}else{
$this->pairX = $this->pairZ = null;
}
}
$this->loadName($nbt);
$this->loadItems($nbt);
}
protected function writeSaveData(CompoundTag $nbt) : void{
if($this->isPaired()){
$nbt->setInt(self::TAG_PAIRX, $this->pairX);
$nbt->setInt(self::TAG_PAIRZ, $this->pairZ);
}
$this->saveName($nbt);
$this->saveItems($nbt);
}
public function getCleanedNBT() : ?CompoundTag{
$tag = parent::getCleanedNBT();
if($tag !== null){
//TODO: replace this with a purpose flag on writeSaveData()
$tag->removeTag(self::TAG_PAIRX, self::TAG_PAIRZ);
}
return $tag;
}
public function close() : void{
if(!$this->closed){
$this->inventory->removeAllViewers();
if($this->doubleInventory !== null){
if($this->isPaired() and $this->world->isChunkLoaded($this->pairX >> 4, $this->pairZ >> 4)){
$this->doubleInventory->removeAllViewers();
$this->doubleInventory->invalidate();
if(($pair = $this->getPair()) !== null){
$pair->doubleInventory = null;
}
}
$this->doubleInventory = null;
}
$this->inventory = null;
parent::close();
}
}
protected function onBlockDestroyedHook() : void{
$this->unpair();
$this->containerTraitBlockDestroyedHook();
}
/**
* @return ChestInventory|DoubleChestInventory
*/
public function getInventory(){
if($this->isPaired() and $this->doubleInventory === null){
$this->checkPairing();
}
return $this->doubleInventory instanceof DoubleChestInventory ? $this->doubleInventory : $this->inventory;
}
/**
* @return ChestInventory
*/
public function getRealInventory(){
return $this->inventory;
}
protected function checkPairing(){
if($this->isPaired() and !$this->getWorld()->isInLoadedTerrain(new Vector3($this->pairX, $this->y, $this->pairZ))){
//paired to a tile in an unloaded chunk
$this->doubleInventory = null;
}elseif(($pair = $this->getPair()) instanceof Chest){
if(!$pair->isPaired()){
$pair->createPair($this);
$pair->checkPairing();
}
if($this->doubleInventory === null){
if($pair->doubleInventory !== null){
$this->doubleInventory = $pair->doubleInventory;
}else{
if(($pair->x + ($pair->z << 15)) > ($this->x + ($this->z << 15))){ //Order them correctly
$this->doubleInventory = $pair->doubleInventory = new DoubleChestInventory($pair, $this);
}else{
$this->doubleInventory = $pair->doubleInventory = new DoubleChestInventory($this, $pair);
}
}
}
}else{
$this->doubleInventory = null;
$this->pairX = $this->pairZ = null;
}
}
/**
* @return string
*/
public function getDefaultName() : string{
return "Chest";
}
public function isPaired(){
return $this->pairX !== null and $this->pairZ !== null;
}
/**
* @return Chest|null
*/
public function getPair() : ?Chest{
if($this->isPaired()){
$tile = $this->getWorld()->getTileAt($this->pairX, $this->y, $this->pairZ);
if($tile instanceof Chest){
return $tile;
}
}
return null;
}
public function pairWith(Chest $tile){
if($this->isPaired() or $tile->isPaired()){
return false;
}
$this->createPair($tile);
$this->setDirty();
$tile->setDirty();
$this->checkPairing();
return true;
}
private function createPair(Chest $tile){
$this->pairX = $tile->x;
$this->pairZ = $tile->z;
$tile->pairX = $this->x;
$tile->pairZ = $this->z;
}
public function unpair(){
if(!$this->isPaired()){
return false;
}
$tile = $this->getPair();
$this->pairX = $this->pairZ = null;
$this->setDirty();
if($tile instanceof Chest){
$tile->pairX = $tile->pairZ = null;
$tile->checkPairing();
$tile->setDirty();
}
$this->checkPairing();
return true;
}
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
if($this->isPaired()){
$nbt->setInt(self::TAG_PAIRX, $this->pairX);
$nbt->setInt(self::TAG_PAIRZ, $this->pairZ);
}
$this->addNameSpawnData($nbt);
}
}

View File

@ -0,0 +1,60 @@
<?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\block\tile;
use pocketmine\block\RedstoneComparator;
use pocketmine\nbt\tag\CompoundTag;
/**
* @deprecated
* @see RedstoneComparator
*/
class Comparator extends Tile{
private const TAG_OUTPUT_SIGNAL = "OutputSignal"; //int
/** @var int */
protected $signalStrength = 0;
/**
* @return int
*/
public function getSignalStrength() : int{
return $this->signalStrength;
}
/**
* @param int $signalStrength
*/
public function setSignalStrength(int $signalStrength) : void{
$this->signalStrength = $signalStrength;
}
public function readSaveData(CompoundTag $nbt) : void{
$this->signalStrength = $nbt->getInt(self::TAG_OUTPUT_SIGNAL, 0, true);
}
protected function writeSaveData(CompoundTag $nbt) : void{
$nbt->setInt(self::TAG_OUTPUT_SIGNAL, $this->signalStrength);
}
}

View File

@ -0,0 +1,46 @@
<?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\block\tile;
use pocketmine\inventory\Inventory;
use pocketmine\inventory\InventoryHolder;
interface Container extends InventoryHolder{
public const TAG_ITEMS = "Items";
public const TAG_LOCK = "Lock";
/**
* @return Inventory
*/
public function getRealInventory();
/**
* Returns whether this container can be opened by an item with the given custom name.
*
* @param string $key
*
* @return bool
*/
public function canOpenWith(string $key) : bool;
}

View File

@ -0,0 +1,108 @@
<?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\block\tile;
use pocketmine\inventory\Inventory;
use pocketmine\item\Item;
use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\world\Position;
/**
* This trait implements most methods in the {@link Container} interface. It should only be used by Tiles.
*/
trait ContainerTrait{
/** @var string|null */
private $lock = null;
/**
* @return Inventory
*/
abstract public function getRealInventory();
protected function loadItems(CompoundTag $tag) : void{
if($tag->hasTag(Container::TAG_ITEMS, ListTag::class)){
$inventoryTag = $tag->getListTag(Container::TAG_ITEMS);
$inventory = $this->getRealInventory();
$listeners = $inventory->getChangeListeners();
$inventory->removeChangeListeners(...$listeners); //prevent any events being fired by initialization
$inventory->clearAll();
/** @var CompoundTag $itemNBT */
foreach($inventoryTag as $itemNBT){
$inventory->setItem($itemNBT->getByte("Slot"), Item::nbtDeserialize($itemNBT));
}
$inventory->addChangeListeners(...$listeners);
}
if($tag->hasTag(Container::TAG_LOCK, StringTag::class)){
$this->lock = $tag->getString(Container::TAG_LOCK);
}
}
protected function saveItems(CompoundTag $tag) : void{
$items = [];
foreach($this->getRealInventory()->getContents() as $slot => $item){
$items[] = $item->nbtSerialize($slot);
}
$tag->setTag(Container::TAG_ITEMS, new ListTag($items, NBT::TAG_Compound));
if($this->lock !== null){
$tag->setString(Container::TAG_LOCK, $this->lock);
}
}
/**
* @see Container::canOpenWith()
*
* @param string $key
*
* @return bool
*/
public function canOpenWith(string $key) : bool{
return $this->lock === null or $this->lock === $key;
}
/**
* @see Position::asPosition()
* @return Position
*/
abstract protected function asPosition() : Position;
/**
* @see Tile::onBlockDestroyedHook()
*/
protected function onBlockDestroyedHook() : void{
$inv = $this->getRealInventory();
$pos = $this->asPosition();
foreach($inv->getContents() as $k => $item){
$pos->world->dropItem($pos->add(0.5, 0.5, 0.5), $item);
}
$inv->clearAll(false);
}
}

View File

@ -0,0 +1,44 @@
<?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\block\tile;
use pocketmine\nbt\tag\CompoundTag;
/**
* @deprecated
* As per the wiki, this is an old hack to force daylight sensors to get updated every game tick. This is necessary to
* ensure that the daylight sensor's power output is always up to date with the current world time.
* It's theoretically possible to implement this without a blockentity, but this is here to ensure that vanilla can
* understand daylight sensors in worlds created by PM.
*/
class DaylightSensor extends Tile{
public function readSaveData(CompoundTag $nbt) : void{
}
protected function writeSaveData(CompoundTag $nbt) : void{
}
}

View File

@ -0,0 +1,38 @@
<?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\block\tile;
class EnchantTable extends Spawnable implements Nameable{
use NameableTrait {
loadName as public readSaveData;
saveName as writeSaveData;
}
/**
* @return string
*/
public function getDefaultName() : string{
return "Enchanting Table";
}
}

View File

@ -0,0 +1,41 @@
<?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\block\tile;
use pocketmine\nbt\tag\CompoundTag;
class EnderChest extends Spawnable{
public function readSaveData(CompoundTag $nbt) : void{
}
protected function writeSaveData(CompoundTag $nbt) : void{
}
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
}
}

View File

@ -0,0 +1,81 @@
<?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\block\tile;
use pocketmine\block\Air;
use pocketmine\block\Block;
use pocketmine\block\BlockFactory;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\ShortTag;
/**
* @deprecated
* @see \pocketmine\block\FlowerPot
*/
class FlowerPot extends Spawnable{
private const TAG_ITEM = "item";
private const TAG_ITEM_DATA = "mData";
/** @var Block|null */
private $plant = null;
public function readSaveData(CompoundTag $nbt) : void{
if($nbt->hasTag(self::TAG_ITEM, ShortTag::class) and $nbt->hasTag(self::TAG_ITEM_DATA, IntTag::class)){
try{
$this->setPlant(BlockFactory::get($nbt->getShort(self::TAG_ITEM), $nbt->getInt(self::TAG_ITEM_DATA)));
}catch(\InvalidArgumentException $e){
//noop
}
}else{
//TODO: new PlantBlock tag
}
}
protected function writeSaveData(CompoundTag $nbt) : void{
if($this->plant !== null){
$nbt->setShort(self::TAG_ITEM, $this->plant->getId());
$nbt->setInt(self::TAG_ITEM_DATA, $this->plant->getMeta());
}
}
public function getPlant() : ?Block{
return $this->plant !== null ? clone $this->plant : null;
}
public function setPlant(?Block $plant) : void{
if($plant === null or $plant instanceof Air){
$this->plant = null;
}else{
$this->plant = clone $plant;
}
}
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
if($this->plant !== null){
$nbt->setShort(self::TAG_ITEM, $this->plant->getId());
$nbt->setInt(self::TAG_ITEM_DATA, $this->plant->getMeta());
}
}
}

224
src/block/tile/Furnace.php Normal file
View File

@ -0,0 +1,224 @@
<?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\block\tile;
use pocketmine\block\Furnace as BlockFurnace;
use pocketmine\crafting\FurnaceRecipe;
use pocketmine\event\inventory\FurnaceBurnEvent;
use pocketmine\event\inventory\FurnaceSmeltEvent;
use pocketmine\inventory\CallbackInventoryChangeListener;
use pocketmine\inventory\FurnaceInventory;
use pocketmine\inventory\Inventory;
use pocketmine\item\Item;
use pocketmine\item\ItemFactory;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\protocol\ContainerSetDataPacket;
use pocketmine\world\World;
use function max;
class Furnace extends Spawnable implements Container, Nameable{
use NameableTrait;
use ContainerTrait;
public const TAG_BURN_TIME = "BurnTime";
public const TAG_COOK_TIME = "CookTime";
public const TAG_MAX_TIME = "MaxTime";
/** @var FurnaceInventory */
protected $inventory;
/** @var int */
private $remainingFuelTime = 0;
/** @var int */
private $cookTime = 0;
/** @var int */
private $maxFuelTime = 0;
public function __construct(World $world, Vector3 $pos){
$this->inventory = new FurnaceInventory($this);
$this->inventory->addChangeListeners(CallbackInventoryChangeListener::onAnyChange(
function(Inventory $unused) : void{
$this->world->scheduleDelayedBlockUpdate($this->asVector3(), 1);
})
);
parent::__construct($world, $pos);
}
public function readSaveData(CompoundTag $nbt) : void{
$this->remainingFuelTime = max(0, $nbt->getShort(self::TAG_BURN_TIME, $this->remainingFuelTime, true));
$this->cookTime = $nbt->getShort(self::TAG_COOK_TIME, $this->cookTime, true);
if($this->remainingFuelTime === 0){
$this->cookTime = 0;
}
$this->maxFuelTime = $nbt->getShort(self::TAG_MAX_TIME, $this->maxFuelTime, true);
if($this->maxFuelTime === 0){
$this->maxFuelTime = $this->remainingFuelTime;
}
$this->loadName($nbt);
$this->loadItems($nbt);
}
protected function writeSaveData(CompoundTag $nbt) : void{
$nbt->setShort(self::TAG_BURN_TIME, $this->remainingFuelTime);
$nbt->setShort(self::TAG_COOK_TIME, $this->cookTime);
$nbt->setShort(self::TAG_MAX_TIME, $this->maxFuelTime);
$this->saveName($nbt);
$this->saveItems($nbt);
}
/**
* @return string
*/
public function getDefaultName() : string{
return "Furnace";
}
public function close() : void{
if(!$this->closed){
$this->inventory->removeAllViewers();
$this->inventory = null;
parent::close();
}
}
/**
* @return FurnaceInventory
*/
public function getInventory(){
return $this->inventory;
}
/**
* @return FurnaceInventory
*/
public function getRealInventory(){
return $this->getInventory();
}
protected function checkFuel(Item $fuel){
$ev = new FurnaceBurnEvent($this, $fuel, $fuel->getFuelTime());
$ev->call();
if($ev->isCancelled()){
return;
}
$this->maxFuelTime = $this->remainingFuelTime = $ev->getBurnTime();
$block = $this->getBlock();
if($block instanceof BlockFurnace and !$block->isLit()){
$block->setLit(true);
$this->getWorld()->setBlock($block, $block);
}
if($this->remainingFuelTime > 0 and $ev->isBurning()){
$fuel->pop();
$this->inventory->setFuel($fuel);
}
}
public function onUpdate() : bool{
//TODO: move this to Block
if($this->closed){
return false;
}
$this->timings->startTiming();
$prevCookTime = $this->cookTime;
$prevRemainingFuelTime = $this->remainingFuelTime;
$prevMaxFuelTime = $this->maxFuelTime;
$ret = false;
$fuel = $this->inventory->getFuel();
$raw = $this->inventory->getSmelting();
$product = $this->inventory->getResult();
$smelt = $this->world->getServer()->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->remainingFuelTime <= 0 and $canSmelt and $fuel->getFuelTime() > 0 and $fuel->getCount() > 0){
$this->checkFuel($fuel);
}
if($this->remainingFuelTime > 0){
--$this->remainingFuelTime;
if($smelt instanceof FurnaceRecipe and $canSmelt){
++$this->cookTime;
if($this->cookTime >= 200){ //10 seconds
$product = ItemFactory::get($smelt->getResult()->getId(), $smelt->getResult()->getMeta(), $product->getCount() + 1);
$ev = new FurnaceSmeltEvent($this, $raw, $product);
$ev->call();
if(!$ev->isCancelled()){
$this->inventory->setResult($ev->getResult());
$raw->pop();
$this->inventory->setSmelting($raw);
}
$this->cookTime -= 200;
}
}elseif($this->remainingFuelTime <= 0){
$this->remainingFuelTime = $this->cookTime = $this->maxFuelTime = 0;
}else{
$this->cookTime = 0;
}
$ret = true;
}else{
$block = $this->getBlock();
if($block instanceof BlockFurnace and $block->isLit()){
$block->setLit(false);
$this->getWorld()->setBlock($block, $block);
}
$this->remainingFuelTime = $this->cookTime = $this->maxFuelTime = 0;
}
if($prevCookTime !== $this->cookTime){
foreach($this->inventory->getViewers() as $v){
$v->getNetworkSession()->getInvManager()->syncData($this->inventory, ContainerSetDataPacket::PROPERTY_FURNACE_SMELT_PROGRESS, $this->cookTime);
}
}
if($prevRemainingFuelTime !== $this->remainingFuelTime){
foreach($this->inventory->getViewers() as $v){
$v->getNetworkSession()->getInvManager()->syncData($this->inventory, ContainerSetDataPacket::PROPERTY_FURNACE_REMAINING_FUEL_TIME, $this->remainingFuelTime);
}
}
if($prevMaxFuelTime !== $this->maxFuelTime){
foreach($this->inventory->getViewers() as $v){
$v->getNetworkSession()->getInvManager()->syncData($this->inventory, ContainerSetDataPacket::PROPERTY_FURNACE_MAX_FUEL_TIME, $this->maxFuelTime);
}
}
$this->timings->stopTiming();
return $ret;
}
}

89
src/block/tile/Hopper.php Normal file
View File

@ -0,0 +1,89 @@
<?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\block\tile;
use pocketmine\inventory\HopperInventory;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\world\World;
class Hopper extends Spawnable implements Container, Nameable{
use ContainerTrait;
use NameableTrait;
private const TAG_TRANSFER_COOLDOWN = "TransferCooldown";
/** @var HopperInventory */
private $inventory;
/** @var int */
private $transferCooldown = 0;
public function __construct(World $world, Vector3 $pos){
$this->inventory = new HopperInventory($this);
parent::__construct($world, $pos);
}
public function readSaveData(CompoundTag $nbt) : void{
$this->loadItems($nbt);
$this->loadName($nbt);
$this->transferCooldown = $nbt->getInt(self::TAG_TRANSFER_COOLDOWN, 0);
}
protected function writeSaveData(CompoundTag $nbt) : void{
$this->saveItems($nbt);
$this->saveName($nbt);
$nbt->setInt(self::TAG_TRANSFER_COOLDOWN, $this->transferCooldown);
}
public function close() : void{
if(!$this->closed){
$this->inventory->removeAllViewers();
$this->inventory = null;
parent::close();
}
}
public function getDefaultName() : string{
return "Hopper";
}
/**
* @return HopperInventory
*/
public function getInventory(){
return $this->inventory;
}
/**
* @return HopperInventory
*/
public function getRealInventory(){
return $this->inventory;
}
}

View File

@ -0,0 +1,104 @@
<?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\block\tile;
use pocketmine\item\Item;
use pocketmine\item\ItemFactory;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\world\World;
/**
* @deprecated
* @see \pocketmine\block\ItemFrame
*/
class ItemFrame extends Spawnable{
public const TAG_ITEM_ROTATION = "ItemRotation";
public const TAG_ITEM_DROP_CHANCE = "ItemDropChance";
public const TAG_ITEM = "Item";
/** @var Item */
private $item;
/** @var int */
private $itemRotation = 0;
/** @var float */
private $itemDropChance = 1.0;
public function __construct(World $world, Vector3 $pos){
$this->item = ItemFactory::air();
parent::__construct($world, $pos);
}
public function readSaveData(CompoundTag $nbt) : void{
if(($itemTag = $nbt->getCompoundTag(self::TAG_ITEM)) !== null){
$this->item = Item::nbtDeserialize($itemTag);
}
$this->itemRotation = $nbt->getByte(self::TAG_ITEM_ROTATION, $this->itemRotation, true);
$this->itemDropChance = $nbt->getFloat(self::TAG_ITEM_DROP_CHANCE, $this->itemDropChance, true);
}
protected function writeSaveData(CompoundTag $nbt) : void{
$nbt->setFloat(self::TAG_ITEM_DROP_CHANCE, $this->itemDropChance);
$nbt->setByte(self::TAG_ITEM_ROTATION, $this->itemRotation);
$nbt->setTag(self::TAG_ITEM, $this->item->nbtSerialize());
}
public function hasItem() : bool{
return !$this->item->isNull();
}
public function getItem() : Item{
return clone $this->item;
}
public function setItem(?Item $item) : void{
if($item !== null and !$item->isNull()){
$this->item = clone $item;
}else{
$this->item = ItemFactory::air();
}
}
public function getItemRotation() : int{
return $this->itemRotation;
}
public function setItemRotation(int $rotation) : void{
$this->itemRotation = $rotation;
}
public function getItemDropChance() : float{
return $this->itemDropChance;
}
public function setItemDropChance(float $chance) : void{
$this->itemDropChance = $chance;
}
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
$nbt->setFloat(self::TAG_ITEM_DROP_CHANCE, $this->itemDropChance);
$nbt->setByte(self::TAG_ITEM_ROTATION, $this->itemRotation);
$nbt->setTag(self::TAG_ITEM, $this->item->nbtSerialize());
}
}

View File

@ -0,0 +1,152 @@
<?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\block\tile;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\network\mcpe\protocol\AddActorPacket;
/**
* @deprecated
*/
class MonsterSpawner extends Spawnable{
private const TAG_LEGACY_ENTITY_TYPE_ID = "EntityId"; //TAG_Int
private const TAG_ENTITY_TYPE_ID = "EntityIdentifier"; //TAG_String
private const TAG_SPAWN_DELAY = "Delay"; //TAG_Short
private const TAG_SPAWN_POTENTIALS = "SpawnPotentials"; //TAG_List<TAG_Compound>
private const TAG_SPAWN_DATA = "SpawnData"; //TAG_Compound
private const TAG_MIN_SPAWN_DELAY = "MinSpawnDelay"; //TAG_Short
private const TAG_MAX_SPAWN_DELAY = "MaxSpawnDelay"; //TAG_Short
private const TAG_SPAWN_PER_ATTEMPT = "SpawnCount"; //TAG_Short
private const TAG_MAX_NEARBY_ENTITIES = "MaxNearbyEntities"; //TAG_Short
private const TAG_REQUIRED_PLAYER_RANGE = "RequiredPlayerRange"; //TAG_Short
private const TAG_SPAWN_RANGE = "SpawnRange"; //TAG_Short
private const TAG_ENTITY_WIDTH = "DisplayEntityWidth"; //TAG_Float
private const TAG_ENTITY_HEIGHT = "DisplayEntityHeight"; //TAG_Float
private const TAG_ENTITY_SCALE = "DisplayEntityScale"; //TAG_Float
public const DEFAULT_MIN_SPAWN_DELAY = 200; //ticks
public const DEFAULT_MAX_SPAWN_DELAY = 800;
public const DEFAULT_MAX_NEARBY_ENTITIES = 6;
public const DEFAULT_SPAWN_RANGE = 4; //blocks
public const DEFAULT_REQUIRED_PLAYER_RANGE = 16;
/**
* @var string
* TODO: replace this with a cached entity or something of that nature
*/
private $entityTypeId = ":";
/**
* @var ListTag|null
* TODO: deserialize this properly and drop the NBT (PC and PE formats are different, just for fun)
*/
private $spawnPotentials = null;
/**
* @var CompoundTag|null
* TODO: deserialize this properly and drop the NBT (PC and PE formats are different, just for fun)
*/
private $spawnData = null;
/** @var float */
private $displayEntityWidth = 1;
/** @var float */
private $displayEntityHeight = 1;
/** @var float */
private $displayEntityScale = 1;
/** @var int */
private $spawnDelay = self::DEFAULT_MIN_SPAWN_DELAY;
/** @var int */
private $minSpawnDelay = self::DEFAULT_MIN_SPAWN_DELAY;
/** @var int */
private $maxSpawnDelay = self::DEFAULT_MAX_SPAWN_DELAY;
/** @var int */
private $spawnPerAttempt = 1;
/** @var int */
private $maxNearbyEntities = self::DEFAULT_MAX_NEARBY_ENTITIES;
/** @var int */
private $spawnRange = self::DEFAULT_SPAWN_RANGE;
/** @var int */
private $requiredPlayerRange = self::DEFAULT_REQUIRED_PLAYER_RANGE;
public function readSaveData(CompoundTag $nbt) : void{
if($nbt->hasTag(self::TAG_LEGACY_ENTITY_TYPE_ID, IntTag::class)){
//TODO: this will cause unexpected results when there's no mapping for the entity
$this->entityTypeId = AddActorPacket::LEGACY_ID_MAP_BC[$nbt->getInt(self::TAG_LEGACY_ENTITY_TYPE_ID)] ?? ":";
}elseif($nbt->hasTag(self::TAG_ENTITY_TYPE_ID, StringTag::class)){
$this->entityTypeId = $nbt->getString(self::TAG_ENTITY_TYPE_ID);
}else{
$this->entityTypeId = ":"; //default - TODO: replace this with a constant
}
$this->spawnData = $nbt->getCompoundTag(self::TAG_SPAWN_DATA);
$this->spawnPotentials = $nbt->getListTag(self::TAG_SPAWN_POTENTIALS);
$this->spawnDelay = $nbt->getShort(self::TAG_SPAWN_DELAY, self::DEFAULT_MIN_SPAWN_DELAY);
$this->minSpawnDelay = $nbt->getShort(self::TAG_MIN_SPAWN_DELAY, self::DEFAULT_MIN_SPAWN_DELAY);
$this->maxSpawnDelay = $nbt->getShort(self::TAG_MAX_SPAWN_DELAY, self::DEFAULT_MAX_SPAWN_DELAY);
$this->spawnPerAttempt = $nbt->getShort(self::TAG_SPAWN_PER_ATTEMPT, 1);
$this->maxNearbyEntities = $nbt->getShort(self::TAG_MAX_NEARBY_ENTITIES, self::DEFAULT_MAX_NEARBY_ENTITIES);
$this->requiredPlayerRange = $nbt->getShort(self::TAG_REQUIRED_PLAYER_RANGE, self::DEFAULT_REQUIRED_PLAYER_RANGE);
$this->spawnRange = $nbt->getShort(self::TAG_SPAWN_RANGE, self::DEFAULT_SPAWN_RANGE);
$this->displayEntityWidth = $nbt->getFloat(self::TAG_ENTITY_WIDTH, 1.0);
$this->displayEntityHeight = $nbt->getFloat(self::TAG_ENTITY_HEIGHT, 1.0);
$this->displayEntityScale = $nbt->getFloat(self::TAG_ENTITY_SCALE, 1.0);
}
protected function writeSaveData(CompoundTag $nbt) : void{
$nbt->setString(self::TAG_ENTITY_TYPE_ID, $this->entityTypeId);
if($this->spawnData !== null){
$nbt->setTag(self::TAG_SPAWN_DATA, clone $this->spawnData);
}
if($this->spawnPotentials !== null){
$nbt->setTag(self::TAG_SPAWN_POTENTIALS, clone $this->spawnPotentials);
}
$nbt->setShort(self::TAG_SPAWN_DELAY, $this->spawnDelay);
$nbt->setShort(self::TAG_MIN_SPAWN_DELAY, $this->minSpawnDelay);
$nbt->setShort(self::TAG_MAX_SPAWN_DELAY, $this->maxSpawnDelay);
$nbt->setShort(self::TAG_SPAWN_PER_ATTEMPT, $this->spawnPerAttempt);
$nbt->setShort(self::TAG_MAX_NEARBY_ENTITIES, $this->maxNearbyEntities);
$nbt->setShort(self::TAG_REQUIRED_PLAYER_RANGE, $this->requiredPlayerRange);
$nbt->setShort(self::TAG_SPAWN_RANGE, $this->spawnRange);
$nbt->setFloat(self::TAG_ENTITY_WIDTH, $this->displayEntityWidth);
$nbt->setFloat(self::TAG_ENTITY_HEIGHT, $this->displayEntityHeight);
$nbt->setFloat(self::TAG_ENTITY_SCALE, $this->displayEntityScale);
}
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
$nbt->setString(self::TAG_ENTITY_TYPE_ID, $this->entityTypeId);
//TODO: we can't set SpawnData here because it might crash the client if it's from a PC world (we need to implement full deserialization)
$nbt->setFloat(self::TAG_ENTITY_SCALE, $this->displayEntityScale);
}
}

View File

@ -0,0 +1,48 @@
<?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\block\tile;
interface Nameable{
public const TAG_CUSTOM_NAME = "CustomName";
/**
* @return string
*/
public function getDefaultName() : string;
/**
* @return string
*/
public function getName() : string;
/**
* @param string $str
*/
public function setName(string $str) : void;
/**
* @return bool
*/
public function hasName() : bool;
}

View File

@ -0,0 +1,95 @@
<?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\block\tile;
use pocketmine\item\Item;
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.
*/
trait NameableTrait{
/** @var string|null */
private $customName = null;
/**
* @return string
*/
abstract public function getDefaultName() : string;
/**
* @return string
*/
public function getName() : string{
return $this->customName ?? $this->getDefaultName();
}
/**
* @param string $name
*/
public function setName(string $name) : void{
if($name === ""){
$this->customName = null;
}else{
$this->customName = $name;
}
}
/**
* @return bool
*/
public function hasName() : bool{
return $this->customName !== null;
}
public function addAdditionalSpawnData(CompoundTag $nbt) : void{
if($this->customName !== null){
$nbt->setString(Nameable::TAG_CUSTOM_NAME, $this->customName);
}
}
protected function loadName(CompoundTag $tag) : void{
if($tag->hasTag(Nameable::TAG_CUSTOM_NAME, StringTag::class)){
$this->customName = $tag->getString(Nameable::TAG_CUSTOM_NAME);
}
}
protected function saveName(CompoundTag $tag) : void{
if($this->customName !== null){
$tag->setString(Nameable::TAG_CUSTOM_NAME, $this->customName);
}
}
/**
* @param Item $item
* @see Tile::copyDataFromItem()
*/
public function copyDataFromItem(Item $item) : void{
parent::copyDataFromItem($item);
if($item->hasCustomName()){ //this should take precedence over saved NBT
$this->setName($item->getCustomName());
}
}
}

62
src/block/tile/Note.php Normal file
View File

@ -0,0 +1,62 @@
<?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\block\tile;
use pocketmine\block\Note as BlockNote;
use pocketmine\nbt\tag\CompoundTag;
/**
* @deprecated
*/
class Note extends Tile{
/** @var int */
private $pitch = 0;
public function readSaveData(CompoundTag $nbt) : void{
if(($pitch = $nbt->getByte("note", $this->pitch)) > BlockNote::MIN_PITCH and $pitch <= BlockNote::MAX_PITCH){
$this->pitch = $pitch;
}
}
protected function writeSaveData(CompoundTag $nbt) : void{
$nbt->setByte("note", $this->pitch);
}
/**
* @return int
*/
public function getPitch() : int{
return $this->pitch;
}
/**
* @param int $pitch
*/
public function setPitch(int $pitch) : void{
if($pitch < BlockNote::MIN_PITCH or $pitch > BlockNote::MAX_PITCH){
throw new \InvalidArgumentException("Pitch must be in range " . BlockNote::MIN_PITCH . " - " . BlockNote::MAX_PITCH);
}
$this->pitch = $pitch;
}
}

98
src/block/tile/Sign.php Normal file
View File

@ -0,0 +1,98 @@
<?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\block\tile;
use pocketmine\block\utils\SignText;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\world\World;
use function array_pad;
use function array_slice;
use function explode;
use function implode;
use function mb_scrub;
use function sprintf;
/**
* @deprecated
* @see \pocketmine\block\Sign
*/
class Sign extends Spawnable{
public const TAG_TEXT_BLOB = "Text";
public const TAG_TEXT_LINE = "Text%d"; //sprintf()able
public static function fixTextBlob(string $blob) : array{
return array_slice(array_pad(explode("\n", $blob), 4, ""), 0, 4);
}
/** @var SignText */
protected $text;
public function __construct(World $world, Vector3 $pos){
$this->text = new SignText();
parent::__construct($world, $pos);
}
public function readSaveData(CompoundTag $nbt) : void{
if($nbt->hasTag(self::TAG_TEXT_BLOB, StringTag::class)){ //MCPE 1.2 save format
$this->text = SignText::fromBlob(mb_scrub($nbt->getString(self::TAG_TEXT_BLOB), 'UTF-8'));
}else{
$this->text = new SignText();
for($i = 0; $i < SignText::LINE_COUNT; ++$i){
$textKey = sprintf(self::TAG_TEXT_LINE, $i + 1);
if($nbt->hasTag($textKey, StringTag::class)){
$this->text->setLine($i, mb_scrub($nbt->getString($textKey), 'UTF-8'));
}
}
}
}
protected function writeSaveData(CompoundTag $nbt) : void{
$nbt->setString(self::TAG_TEXT_BLOB, implode("\n", $this->text->getLines()));
for($i = 0; $i < SignText::LINE_COUNT; ++$i){ //Backwards-compatibility
$textKey = sprintf(self::TAG_TEXT_LINE, $i + 1);
$nbt->setString($textKey, $this->text->getLine($i));
}
}
/**
* @return SignText
*/
public function getText() : SignText{
return $this->text;
}
/**
* @param SignText $text
*/
public function setText(SignText $text) : void{
$this->text = $text;
}
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
$nbt->setString(self::TAG_TEXT_BLOB, implode("\n", $this->text->getLines()));
}
}

92
src/block/tile/Skull.php Normal file
View File

@ -0,0 +1,92 @@
<?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\block\tile;
use pocketmine\block\utils\SkullType;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\world\World;
/**
* @deprecated
* @see \pocketmine\block\Skull
*/
class Skull extends Spawnable{
private const TAG_SKULL_TYPE = "SkullType"; //TAG_Byte
private const TAG_ROT = "Rot"; //TAG_Byte
private const TAG_MOUTH_MOVING = "MouthMoving"; //TAG_Byte
private const TAG_MOUTH_TICK_COUNT = "MouthTickCount"; //TAG_Int
/** @var SkullType */
private $skullType;
/** @var int */
private $skullRotation = 0;
public function __construct(World $world, Vector3 $pos){
$this->skullType = SkullType::SKELETON();
parent::__construct($world, $pos);
}
public function readSaveData(CompoundTag $nbt) : void{
if($nbt->hasTag(self::TAG_SKULL_TYPE, ByteTag::class)){
try{
$this->skullType = SkullType::fromMagicNumber($nbt->getByte(self::TAG_SKULL_TYPE));
}catch(\InvalidArgumentException $e){
//bad data, drop it
}
}
$rotation = $nbt->getByte(self::TAG_ROT, 0, true);
if($rotation >= 0 and $rotation <= 15){
$this->skullRotation = $rotation;
}
}
protected function writeSaveData(CompoundTag $nbt) : void{
$nbt->setByte(self::TAG_SKULL_TYPE, $this->skullType->getMagicNumber());
$nbt->setByte(self::TAG_ROT, $this->skullRotation);
}
public function setSkullType(SkullType $type) : void{
$this->skullType = $type;
}
public function getSkullType() : SkullType{
return $this->skullType;
}
public function getRotation() : int{
return $this->skullRotation;
}
public function setRotation(int $rotation) : void{
$this->skullRotation = $rotation;
}
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
$nbt->setByte(self::TAG_SKULL_TYPE, $this->skullType->getMagicNumber());
$nbt->setByte(self::TAG_ROT, $this->skullRotation);
}
}

View File

@ -0,0 +1,97 @@
<?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\block\tile;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\TreeRoot;
use pocketmine\network\mcpe\serializer\NetworkNbtSerializer;
use function get_class;
abstract class Spawnable extends Tile{
/** @var string|null */
private $spawnCompoundCache = null;
/** @var bool */
private $dirty = true; //default dirty, until it's been spawned appropriately on the world
/** @var NetworkNbtSerializer|null */
private static $nbtWriter = null;
/**
* Returns whether the tile needs to be respawned to viewers.
*
* @return bool
*/
public function isDirty() : bool{
return $this->dirty;
}
/**
* @param bool $dirty
*/
public function setDirty(bool $dirty = true) : void{
if($dirty){
$this->spawnCompoundCache = null;
}
$this->dirty = $dirty;
}
/**
* Returns encoded NBT (varint, little-endian) used to spawn this tile to clients. Uses cache where possible,
* populates cache if it is null.
*
* @return string encoded NBT
*/
final public function getSerializedSpawnCompound() : string{
if($this->spawnCompoundCache === null){
if(self::$nbtWriter === null){
self::$nbtWriter = new NetworkNbtSerializer();
}
$this->spawnCompoundCache = self::$nbtWriter->write(new TreeRoot($this->getSpawnCompound()));
}
return $this->spawnCompoundCache;
}
/**
* @return CompoundTag
*/
final public function getSpawnCompound() : CompoundTag{
$nbt = CompoundTag::create()
->setString(self::TAG_ID, TileFactory::getSaveId(get_class($this))) //TODO: disassociate network ID from save ID
->setInt(self::TAG_X, $this->x)
->setInt(self::TAG_Y, $this->y)
->setInt(self::TAG_Z, $this->z);
$this->addAdditionalSpawnData($nbt);
return $nbt;
}
/**
* An extension to getSpawnCompound() for
* further modifying the generic tile NBT.
*
* @param CompoundTag $nbt
*/
abstract protected function addAdditionalSpawnData(CompoundTag $nbt) : void;
}

141
src/block/tile/Tile.php Normal file
View File

@ -0,0 +1,141 @@
<?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);
/**
* All the Tile classes and related classes
*/
namespace pocketmine\block\tile;
use pocketmine\block\Block;
use pocketmine\item\Item;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\timings\Timings;
use pocketmine\timings\TimingsHandler;
use pocketmine\world\Position;
use pocketmine\world\World;
use function get_class;
abstract class Tile extends Position{
public const TAG_ID = "id";
public const TAG_X = "x";
public const TAG_Y = "y";
public const TAG_Z = "z";
/** @var bool */
public $closed = false;
/** @var TimingsHandler */
protected $timings;
public function __construct(World $world, Vector3 $pos){
$this->timings = Timings::getTileEntityTimings($this);
parent::__construct($pos->getFloorX(), $pos->getFloorY(), $pos->getFloorZ(), $world);
}
/**
* @internal
* Reads additional data from the CompoundTag on tile creation.
*
* @param CompoundTag $nbt
*/
abstract public function readSaveData(CompoundTag $nbt) : void;
/**
* Writes additional save data to a CompoundTag, not including generic things like ID and coordinates.
*
* @param CompoundTag $nbt
*/
abstract protected function writeSaveData(CompoundTag $nbt) : void;
public function saveNBT() : CompoundTag{
$nbt = CompoundTag::create()
->setString(self::TAG_ID, TileFactory::getSaveId(get_class($this)))
->setInt(self::TAG_X, $this->x)
->setInt(self::TAG_Y, $this->y)
->setInt(self::TAG_Z, $this->z);
$this->writeSaveData($nbt);
return $nbt;
}
public function getCleanedNBT() : ?CompoundTag{
$this->writeSaveData($tag = new CompoundTag());
return $tag->getCount() > 0 ? $tag : null;
}
/**
* @internal
*
* @param Item $item
*
* @throws \RuntimeException
*/
public function copyDataFromItem(Item $item) : void{
if($item->hasCustomBlockData()){ //TODO: check item root tag (MCPE doesn't use BlockEntityTag)
$this->readSaveData($item->getCustomBlockData());
}
}
/**
* @return Block
*/
public function getBlock() : Block{
return $this->world->getBlockAt($this->x, $this->y, $this->z);
}
public function isClosed() : bool{
return $this->closed;
}
public function __destruct(){
$this->close();
}
/**
* Called when the tile's block is destroyed.
*/
final public function onBlockDestroyed() : void{
$this->onBlockDestroyedHook();
$this->close();
}
/**
* Override this method to do actions you need to do when this tile is destroyed due to block being broken.
*/
protected function onBlockDestroyedHook() : void{
}
public function close() : void{
if(!$this->closed){
$this->closed = true;
if($this->isValid()){
$this->world->removeTile($this);
$this->setWorld(null);
}
}
}
}

View File

@ -0,0 +1,182 @@
<?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\block\tile;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\utils\Utils;
use pocketmine\world\World;
use function assert;
use function in_array;
use function is_a;
use function reset;
final class TileFactory{
/** @var string[] classes that extend Tile */
private static $knownTiles = [];
/** @var string[][] */
private static $saveNames = [];
/** @var string[] base class => overridden class */
private static $classMapping = [];
private function __construct(){
//NOOP
}
public static function init() : void{
self::register(Banner::class, ["Banner", "minecraft:banner"]);
self::register(Bed::class, ["Bed", "minecraft:bed"]);
self::register(BrewingStand::class, ["BrewingStand", "minecraft:brewing_stand"]);
self::register(Chest::class, ["Chest", "minecraft:chest"]);
self::register(Comparator::class, ["Comparator", "minecraft:comparator"]);
self::register(DaylightSensor::class, ["DaylightDetector", "minecraft:daylight_detector"]);
self::register(EnchantTable::class, ["EnchantTable", "minecraft:enchanting_table"]);
self::register(EnderChest::class, ["EnderChest", "minecraft:ender_chest"]);
self::register(FlowerPot::class, ["FlowerPot", "minecraft:flower_pot"]);
self::register(Furnace::class, ["Furnace", "minecraft:furnace"]);
self::register(Hopper::class, ["Hopper", "minecraft:hopper"]);
self::register(ItemFrame::class, ["ItemFrame"]); //this is an entity in PC
self::register(MonsterSpawner::class, ["MobSpawner", "minecraft:mob_spawner"]);
self::register(Note::class, ["Music", "minecraft:noteblock"]);
self::register(Sign::class, ["Sign", "minecraft:sign"]);
self::register(Skull::class, ["Skull", "minecraft:skull"]);
//TODO: Barrel
//TODO: Beacon
//TODO: Bell
//TODO: BlastFurnace
//TODO: Campfire
//TODO: Cauldron
//TODO: ChalkboardBlock
//TODO: ChemistryTable
//TODO: CommandBlock
//TODO: Conduit
//TODO: Dispenser
//TODO: Dropper
//TODO: EndGateway
//TODO: EndPortal
//TODO: JigsawBlock
//TODO: Jukebox
//TODO: Lectern
//TODO: MovingBlock
//TODO: NetherReactor
//TODO: PistonArm
//TODO: ShulkerBox
//TODO: Smoker
//TODO: StructureBlock
}
/**
* @param string $className
* @param string[] $saveNames
*/
public static function register(string $className, array $saveNames = []) : void{
Utils::testValidInstance($className, Tile::class);
self::$classMapping[$className] = $className;
$shortName = (new \ReflectionClass($className))->getShortName();
if(!in_array($shortName, $saveNames, true)){
$saveNames[] = $shortName;
}
foreach($saveNames as $name){
self::$knownTiles[$name] = $className;
}
self::$saveNames[$className] = $saveNames;
}
/**
* @param string $baseClass Already-registered tile class to override
* @param string $newClass Class which extends the base class
*
* @throws \InvalidArgumentException if the base class is not a registered tile
*/
public static function override(string $baseClass, string $newClass) : void{
if(!isset(self::$classMapping[$baseClass])){
throw new \InvalidArgumentException("Class $baseClass is not a registered tile");
}
Utils::testValidInstance($newClass, $baseClass);
self::$classMapping[$baseClass] = $newClass;
}
/**
* @param string $baseClass
* @param World $world
* @param Vector3 $pos
*
* @return Tile (will be an instanceof $baseClass)
* @throws \InvalidArgumentException if the specified class is not a registered tile
*/
public static function create(string $baseClass, World $world, Vector3 $pos) : Tile{
if(isset(self::$classMapping[$baseClass])){
$class = self::$classMapping[$baseClass];
assert(is_a($class, $baseClass, true));
/**
* @var Tile $tile
* @see Tile::__construct()
*/
$tile = new $class($world, $pos);
return $tile;
}
throw new \InvalidArgumentException("Class $baseClass is not a registered tile");
}
/**
* @param World $world
* @param CompoundTag $nbt
*
* @return Tile|null
*@internal
*
*/
public static function createFromData(World $world, CompoundTag $nbt) : ?Tile{
$type = $nbt->getString(Tile::TAG_ID, "", true);
if(!isset(self::$knownTiles[$type])){
return null;
}
$class = self::$knownTiles[$type];
assert(is_a($class, Tile::class, true));
/**
* @var Tile $tile
* @see Tile::__construct()
*/
$tile = new $class($world, new Vector3($nbt->getInt(Tile::TAG_X), $nbt->getInt(Tile::TAG_Y), $nbt->getInt(Tile::TAG_Z)));
$tile->readSaveData($nbt);
return $tile;
}
public static function getSaveId(string $class) : string{
if(isset(self::$saveNames[$class])){
return reset(self::$saveNames[$class]);
}
throw new \InvalidArgumentException("Tile $class is not registered");
}
}