diff --git a/src/pocketmine/block/Bed.php b/src/pocketmine/block/Bed.php index 28aaa8b5d..74474e049 100644 --- a/src/pocketmine/block/Bed.php +++ b/src/pocketmine/block/Bed.php @@ -86,13 +86,10 @@ class Bed extends Transparent{ public function writeStateToWorld() : void{ parent::writeStateToWorld(); //extra block properties storage hack - $tile = Tile::create(Tile::BED, $this->getLevel(), $this->asVector3()); - if($tile !== null){ - if($tile instanceof TileBed){ - $tile->setColor($this->color); - } - $this->level->addTile($tile); - } + /** @var TileBed $tile */ + $tile = Tile::create(TileBed::class, $this->getLevel(), $this->asVector3()); + $tile->setColor($this->color); + $this->level->addTile($tile); } public function getHardness() : float{ diff --git a/src/pocketmine/block/Chest.php b/src/pocketmine/block/Chest.php index b577ff66b..f64fe4bb1 100644 --- a/src/pocketmine/block/Chest.php +++ b/src/pocketmine/block/Chest.php @@ -72,7 +72,8 @@ class Chest extends Transparent{ } public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{ - $chest = null; + /** @var TileChest|null $pair */ + $pair = null; if($player !== null){ $this->facing = Facing::opposite($player->getHorizontalFacing()); } @@ -85,21 +86,20 @@ class Chest extends Transparent{ if($c instanceof Chest and $c->isSameType($this) and $c->facing === $this->facing){ $tile = $this->getLevel()->getTile($c); if($tile instanceof TileChest and !$tile->isPaired()){ - $chest = $tile; + $pair = $tile; break; } } } if(parent::place($item, $blockReplace, $blockClicked, $face, $clickVector, $player)){ - $tile = Tile::createFromItem(Tile::CHEST, $this->getLevel(), $this->asVector3(), $item); - if($tile !== null){ - $this->level->addTile($tile); - } + /** @var TileChest $tile */ + $tile = Tile::createFromItem(TileChest::class, $this->getLevel(), $this->asVector3(), $item); + $this->level->addTile($tile); - if($chest instanceof TileChest and $tile instanceof TileChest){ - $chest->pairWith($tile); - $tile->pairWith($chest); + if($pair instanceof TileChest){ + $pair->pairWith($tile); + $tile->pairWith($pair); } return true; diff --git a/src/pocketmine/block/EnchantingTable.php b/src/pocketmine/block/EnchantingTable.php index 02c3059b1..86a88a8c3 100644 --- a/src/pocketmine/block/EnchantingTable.php +++ b/src/pocketmine/block/EnchantingTable.php @@ -30,6 +30,7 @@ use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\Player; +use pocketmine\tile\EnchantTable as TileEnchantingTable; use pocketmine\tile\Tile; class EnchantingTable extends Transparent{ @@ -42,9 +43,7 @@ class EnchantingTable extends Transparent{ public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{ if(parent::place($item, $blockReplace, $blockClicked, $face, $clickVector, $player)){ - if(($tile = Tile::createFromItem(Tile::ENCHANT_TABLE, $this->getLevel(), $this->asVector3(), $item)) !== null){ - $this->level->addTile($tile); - } + $this->level->addTile(Tile::createFromItem(TileEnchantingTable::class, $this->getLevel(), $this->asVector3(), $item)); return true; } diff --git a/src/pocketmine/block/EnderChest.php b/src/pocketmine/block/EnderChest.php index c810263a6..e8026a5af 100644 --- a/src/pocketmine/block/EnderChest.php +++ b/src/pocketmine/block/EnderChest.php @@ -66,9 +66,7 @@ class EnderChest extends Chest{ } if(Block::place($item, $blockReplace, $blockClicked, $face, $clickVector, $player)){ - if(($tile = Tile::createFromItem(Tile::ENDER_CHEST, $this->getLevel(), $this->asVector3(), $item)) !== null){ - $this->level->addTile($tile); - } + $this->level->addTile(Tile::createFromItem(TileEnderChest::class, $this->getLevel(), $this->asVector3(), $item)); return true; } diff --git a/src/pocketmine/block/FlowerPot.php b/src/pocketmine/block/FlowerPot.php index 1c0350dd0..f9ac6eb60 100644 --- a/src/pocketmine/block/FlowerPot.php +++ b/src/pocketmine/block/FlowerPot.php @@ -69,9 +69,7 @@ class FlowerPot extends Flowable{ } if(parent::place($item, $blockReplace, $blockClicked, $face, $clickVector, $player)){ - if(($tile = Tile::createFromItem(Tile::FLOWER_POT, $this->getLevel(), $this->asVector3(), $item)) !== null){ - $this->level->addTile($tile); - } + $this->level->addTile(Tile::createFromItem(TileFlowerPot::class, $this->getLevel(), $this->asVector3(), $item)); return true; } diff --git a/src/pocketmine/block/Furnace.php b/src/pocketmine/block/Furnace.php index b4a91ccf5..96293cd20 100644 --- a/src/pocketmine/block/Furnace.php +++ b/src/pocketmine/block/Furnace.php @@ -99,9 +99,7 @@ class Furnace extends Solid{ $this->facing = Facing::opposite($player->getHorizontalFacing()); } if(parent::place($item, $blockReplace, $blockClicked, $face, $clickVector, $player)){ - if(($tile = Tile::createFromItem(Tile::FURNACE, $this->getLevel(), $this->asVector3(), $item)) !== null){ - $this->level->addTile($tile); - } + $this->level->addTile(Tile::createFromItem(TileFurnace::class, $this->getLevel(), $this->asVector3(), $item)); return true; } diff --git a/src/pocketmine/block/ItemFrame.php b/src/pocketmine/block/ItemFrame.php index adbc93d07..7c6a92abc 100644 --- a/src/pocketmine/block/ItemFrame.php +++ b/src/pocketmine/block/ItemFrame.php @@ -85,9 +85,7 @@ class ItemFrame extends Flowable{ $this->facing = $face; if(parent::place($item, $blockReplace, $blockClicked, $face, $clickVector, $player)){ - if(($tile = Tile::createFromItem(Tile::ITEM_FRAME, $this->getLevel(), $this->asVector3(), $item)) !== null){ - $this->level->addTile($tile); - } + $this->level->addTile(Tile::createFromItem(TileItemFrame::class, $this->getLevel(), $this->asVector3(), $item)); return true; } diff --git a/src/pocketmine/block/SignPost.php b/src/pocketmine/block/SignPost.php index 8f6e027d2..9a420519a 100644 --- a/src/pocketmine/block/SignPost.php +++ b/src/pocketmine/block/SignPost.php @@ -28,6 +28,7 @@ use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\Player; +use pocketmine\tile\Sign as TileSign; use pocketmine\tile\Tile; class SignPost extends Transparent{ @@ -82,9 +83,7 @@ class SignPost extends Transparent{ } if($ret){ - if(($tile = Tile::createFromItem(Tile::SIGN, $this->getLevel(), $this->asVector3(), $item)) !== null){ - $this->level->addTile($tile); - } + $this->level->addTile(Tile::createFromItem(TileSign::class, $this->getLevel(), $this->asVector3(), $item)); return true; } } diff --git a/src/pocketmine/block/Skull.php b/src/pocketmine/block/Skull.php index 7e54d629f..d1fd2955b 100644 --- a/src/pocketmine/block/Skull.php +++ b/src/pocketmine/block/Skull.php @@ -70,14 +70,11 @@ class Skull extends Flowable{ public function writeStateToWorld() : void{ parent::writeStateToWorld(); - $tile = Tile::create(Tile::SKULL, $this->getLevel(), $this->asVector3()); - if($tile !== null){ - if($tile instanceof TileSkull){ - $tile->setRotation($this->rotation); - $tile->setType($this->type); - } - $this->level->addTile($tile); - } + /** @var TileSkull $tile */ + $tile = Tile::create(TileSkull::class, $this->getLevel(), $this->asVector3()); + $tile->setRotation($this->rotation); + $tile->setType($this->type); + $this->level->addTile($tile); } public function getHardness() : float{ diff --git a/src/pocketmine/block/StandingBanner.php b/src/pocketmine/block/StandingBanner.php index 55745c29d..3954b35f9 100644 --- a/src/pocketmine/block/StandingBanner.php +++ b/src/pocketmine/block/StandingBanner.php @@ -84,17 +84,16 @@ class StandingBanner extends Transparent{ } if($ret){ - $tile = Tile::createFromItem(Tile::BANNER, $this->getLevel(), $this->asVector3(), $item); - if($tile !== null){ - if($tile instanceof TileBanner and $item instanceof ItemBanner){ - $tile->setBaseColor($item->getBaseColor()); - if(($patterns = $item->getPatterns()) !== null){ - $tile->setPatterns($patterns); - } + /** @var TileBanner $tile */ + $tile = Tile::createFromItem(TileBanner::class, $this->getLevel(), $this->asVector3(), $item); + if($item instanceof ItemBanner){ + $tile->setBaseColor($item->getBaseColor()); + if(($patterns = $item->getPatterns()) !== null){ + $tile->setPatterns($patterns); } - - $this->level->addTile($tile); } + + $this->level->addTile($tile); return true; } } diff --git a/src/pocketmine/tile/Sign.php b/src/pocketmine/tile/Sign.php index 8d63d7a65..aea189a4f 100644 --- a/src/pocketmine/tile/Sign.php +++ b/src/pocketmine/tile/Sign.php @@ -125,10 +125,6 @@ class Sign extends Spawnable{ } public function updateCompoundTag(CompoundTag $nbt, Player $player) : bool{ - if($nbt->getString("id") !== Tile::SIGN){ - return false; - } - if($nbt->hasTag(self::TAG_TEXT_BLOB, StringTag::class)){ $lines = array_pad(explode("\n", $nbt->getString(self::TAG_TEXT_BLOB)), 4, ""); }else{ diff --git a/src/pocketmine/tile/Tile.php b/src/pocketmine/tile/Tile.php index f6808cc1f..8708ce6ba 100644 --- a/src/pocketmine/tile/Tile.php +++ b/src/pocketmine/tile/Tile.php @@ -44,24 +44,14 @@ abstract class Tile extends Position{ public const TAG_Y = "y"; public const TAG_Z = "z"; - public const BANNER = "Banner"; - public const BED = "Bed"; - public const BREWING_STAND = "BrewingStand"; - public const CHEST = "Chest"; - public const ENCHANT_TABLE = "EnchantTable"; - public const ENDER_CHEST = "EnderChest"; - public const FLOWER_POT = "FlowerPot"; - public const FURNACE = "Furnace"; - public const ITEM_FRAME = "ItemFrame"; - public const MOB_SPAWNER = "MobSpawner"; - public const SIGN = "Sign"; - public const SKULL = "Skull"; - /** @var string[] classes that extend Tile */ private static $knownTiles = []; /** @var string[][] */ private static $saveNames = []; + /** @var string[] base class => overridden class */ + private static $classMapping = []; + /** @var string */ public $name = ""; /** @var bool */ @@ -70,16 +60,16 @@ abstract class Tile extends Position{ protected $timings; public static function init(){ - self::registerTile(Banner::class, [self::BANNER, "minecraft:banner"]); - self::registerTile(Bed::class, [self::BED, "minecraft:bed"]); - self::registerTile(Chest::class, [self::CHEST, "minecraft:chest"]); - self::registerTile(EnchantTable::class, [self::ENCHANT_TABLE, "minecraft:enchanting_table"]); - self::registerTile(EnderChest::class, [self::ENDER_CHEST, "minecraft:ender_chest"]); - self::registerTile(FlowerPot::class, [self::FLOWER_POT, "minecraft:flower_pot"]); - self::registerTile(Furnace::class, [self::FURNACE, "minecraft:furnace"]); - self::registerTile(ItemFrame::class, [self::ITEM_FRAME]); //this is an entity in PC - self::registerTile(Sign::class, [self::SIGN, "minecraft:sign"]); - self::registerTile(Skull::class, [self::SKULL, "minecraft:skull"]); + self::register(Banner::class, ["Banner", "minecraft:banner"]); + self::register(Bed::class, ["Bed", "minecraft:bed"]); + self::register(Chest::class, ["Chest", "minecraft:chest"]); + 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(ItemFrame::class, ["ItemFrame"]); //this is an entity in PC + self::register(Sign::class, ["Sign", "minecraft:sign"]); + self::register(Skull::class, ["Skull", "minecraft:skull"]); } /** @@ -90,27 +80,32 @@ abstract class Tile extends Position{ */ public static function createFromData(Level $level, CompoundTag $nbt) : ?Tile{ $type = $nbt->getString(self::TAG_ID, "", true); - if($type === ""){ + if(!isset(self::$knownTiles[$type])){ return null; } - $tile = self::create($type, $level, new Vector3($nbt->getInt(self::TAG_X), $nbt->getInt(self::TAG_Y), $nbt->getInt(self::TAG_Z))); - if($tile !== null){ - $tile->readSaveData($nbt); - } + $class = self::$knownTiles[$type]; + assert(is_a($class, Tile::class, true)); + /** + * @var Tile $tile + * @see Tile::__construct() + */ + $tile = new $class($level, new Vector3($nbt->getInt(self::TAG_X), $nbt->getInt(self::TAG_Y), $nbt->getInt(self::TAG_Z))); + $tile->readSaveData($nbt); return $tile; } /** - * @param string $type + * @param string $baseClass * @param Level $level * @param Vector3 $pos * @param Item $item * - * @return Tile|null + * @return Tile (instanceof $baseClass) + * @throws \InvalidArgumentException if the base class is not a registered tile */ - public static function createFromItem(string $type, Level $level, Vector3 $pos, Item $item) : ?Tile{ - $tile = self::create($type, $level, $pos); - if($tile !== null and $item->hasCustomBlockData()){ + public static function createFromItem(string $baseClass, Level $level, Vector3 $pos, Item $item) : Tile{ + $tile = self::create($baseClass, $level, $pos); + if($item->hasCustomBlockData()){ $tile->readSaveData($item->getCustomBlockData()); } if($tile instanceof Nameable and $item->hasCustomName()){ //this should take precedence over saved NBT @@ -120,34 +115,15 @@ abstract class Tile extends Position{ return $tile; } - /** - * @param string $type - * @param Level $level - * @param Vector3 $pos - * - * @return Tile|null - */ - public static function create(string $type, Level $level, Vector3 $pos) : ?Tile{ - if(isset(self::$knownTiles[$type])){ - $class = self::$knownTiles[$type]; - /** - * @var Tile $tile - * @see Tile::__construct() - */ - $tile = new $class($level, $pos); - return $tile; - } - - return null; - } - /** * @param string $className * @param string[] $saveNames */ - public static function registerTile(string $className, array $saveNames = []) : void{ + 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; @@ -160,6 +136,44 @@ abstract class Tile extends Position{ 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 Level $level + * @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, Level $level, 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($level, $pos); + return $tile; + } + + throw new \InvalidArgumentException("Class $baseClass is not a registered tile"); + } + /** * Returns the short save name * @return string