Revamp Tile creation (again)

This breaks down the handling of tile creation even further.
- Introduced a static Tile::override() method to allow overriding the construction class for a specific type of chest. This applies to classes as opposed to save IDs, so you can override Chest::class with MyCustomChest::class and it will take effect for any Chest save ID.
- Removed MCPE stringy save ID constants from public Tile interface. These are now only used for creating saved tiles from a stored chunk, and saving them.
- Renamed Tile::registerTile() to register()
- Tile::create() and Tile::createFromItem() now accept a class parameter instead of a stringy save ID.
- Tile::create() and Tile::createFromItem() were changed to throw \InvalidArgumentException on unknown/unregistered tile types. They also now never return null, but always (except in exception cases) return an object which is an instanceof the base class specified.
This commit is contained in:
Dylan K. Taylor 2018-12-26 19:21:37 +00:00
parent 9f4bb440bd
commit f6983efec1
12 changed files with 103 additions and 110 deletions

View File

@ -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{

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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{

View File

@ -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;
}
}

View File

@ -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{

View File

@ -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