mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-05-11 00:09:39 +00:00
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:
parent
9f4bb440bd
commit
f6983efec1
@ -86,13 +86,10 @@ class Bed extends Transparent{
|
|||||||
public function writeStateToWorld() : void{
|
public function writeStateToWorld() : void{
|
||||||
parent::writeStateToWorld();
|
parent::writeStateToWorld();
|
||||||
//extra block properties storage hack
|
//extra block properties storage hack
|
||||||
$tile = Tile::create(Tile::BED, $this->getLevel(), $this->asVector3());
|
/** @var TileBed $tile */
|
||||||
if($tile !== null){
|
$tile = Tile::create(TileBed::class, $this->getLevel(), $this->asVector3());
|
||||||
if($tile instanceof TileBed){
|
$tile->setColor($this->color);
|
||||||
$tile->setColor($this->color);
|
$this->level->addTile($tile);
|
||||||
}
|
|
||||||
$this->level->addTile($tile);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getHardness() : float{
|
public function getHardness() : float{
|
||||||
|
@ -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{
|
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){
|
if($player !== null){
|
||||||
$this->facing = Facing::opposite($player->getHorizontalFacing());
|
$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){
|
if($c instanceof Chest and $c->isSameType($this) and $c->facing === $this->facing){
|
||||||
$tile = $this->getLevel()->getTile($c);
|
$tile = $this->getLevel()->getTile($c);
|
||||||
if($tile instanceof TileChest and !$tile->isPaired()){
|
if($tile instanceof TileChest and !$tile->isPaired()){
|
||||||
$chest = $tile;
|
$pair = $tile;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(parent::place($item, $blockReplace, $blockClicked, $face, $clickVector, $player)){
|
if(parent::place($item, $blockReplace, $blockClicked, $face, $clickVector, $player)){
|
||||||
$tile = Tile::createFromItem(Tile::CHEST, $this->getLevel(), $this->asVector3(), $item);
|
/** @var TileChest $tile */
|
||||||
if($tile !== null){
|
$tile = Tile::createFromItem(TileChest::class, $this->getLevel(), $this->asVector3(), $item);
|
||||||
$this->level->addTile($tile);
|
$this->level->addTile($tile);
|
||||||
}
|
|
||||||
|
|
||||||
if($chest instanceof TileChest and $tile instanceof TileChest){
|
if($pair instanceof TileChest){
|
||||||
$chest->pairWith($tile);
|
$pair->pairWith($tile);
|
||||||
$tile->pairWith($chest);
|
$tile->pairWith($pair);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -30,6 +30,7 @@ use pocketmine\math\AxisAlignedBB;
|
|||||||
use pocketmine\math\Facing;
|
use pocketmine\math\Facing;
|
||||||
use pocketmine\math\Vector3;
|
use pocketmine\math\Vector3;
|
||||||
use pocketmine\Player;
|
use pocketmine\Player;
|
||||||
|
use pocketmine\tile\EnchantTable as TileEnchantingTable;
|
||||||
use pocketmine\tile\Tile;
|
use pocketmine\tile\Tile;
|
||||||
|
|
||||||
class EnchantingTable extends Transparent{
|
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{
|
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(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::createFromItem(TileEnchantingTable::class, $this->getLevel(), $this->asVector3(), $item));
|
||||||
$this->level->addTile($tile);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,9 +66,7 @@ class EnderChest extends Chest{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(Block::place($item, $blockReplace, $blockClicked, $face, $clickVector, $player)){
|
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::createFromItem(TileEnderChest::class, $this->getLevel(), $this->asVector3(), $item));
|
||||||
$this->level->addTile($tile);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,9 +69,7 @@ class FlowerPot extends Flowable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(parent::place($item, $blockReplace, $blockClicked, $face, $clickVector, $player)){
|
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::createFromItem(TileFlowerPot::class, $this->getLevel(), $this->asVector3(), $item));
|
||||||
$this->level->addTile($tile);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,9 +99,7 @@ class Furnace extends Solid{
|
|||||||
$this->facing = Facing::opposite($player->getHorizontalFacing());
|
$this->facing = Facing::opposite($player->getHorizontalFacing());
|
||||||
}
|
}
|
||||||
if(parent::place($item, $blockReplace, $blockClicked, $face, $clickVector, $player)){
|
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::createFromItem(TileFurnace::class, $this->getLevel(), $this->asVector3(), $item));
|
||||||
$this->level->addTile($tile);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,9 +85,7 @@ class ItemFrame extends Flowable{
|
|||||||
$this->facing = $face;
|
$this->facing = $face;
|
||||||
|
|
||||||
if(parent::place($item, $blockReplace, $blockClicked, $face, $clickVector, $player)){
|
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::createFromItem(TileItemFrame::class, $this->getLevel(), $this->asVector3(), $item));
|
||||||
$this->level->addTile($tile);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ use pocketmine\math\AxisAlignedBB;
|
|||||||
use pocketmine\math\Facing;
|
use pocketmine\math\Facing;
|
||||||
use pocketmine\math\Vector3;
|
use pocketmine\math\Vector3;
|
||||||
use pocketmine\Player;
|
use pocketmine\Player;
|
||||||
|
use pocketmine\tile\Sign as TileSign;
|
||||||
use pocketmine\tile\Tile;
|
use pocketmine\tile\Tile;
|
||||||
|
|
||||||
class SignPost extends Transparent{
|
class SignPost extends Transparent{
|
||||||
@ -82,9 +83,7 @@ class SignPost extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if($ret){
|
if($ret){
|
||||||
if(($tile = Tile::createFromItem(Tile::SIGN, $this->getLevel(), $this->asVector3(), $item)) !== null){
|
$this->level->addTile(Tile::createFromItem(TileSign::class, $this->getLevel(), $this->asVector3(), $item));
|
||||||
$this->level->addTile($tile);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,14 +70,11 @@ class Skull extends Flowable{
|
|||||||
|
|
||||||
public function writeStateToWorld() : void{
|
public function writeStateToWorld() : void{
|
||||||
parent::writeStateToWorld();
|
parent::writeStateToWorld();
|
||||||
$tile = Tile::create(Tile::SKULL, $this->getLevel(), $this->asVector3());
|
/** @var TileSkull $tile */
|
||||||
if($tile !== null){
|
$tile = Tile::create(TileSkull::class, $this->getLevel(), $this->asVector3());
|
||||||
if($tile instanceof TileSkull){
|
$tile->setRotation($this->rotation);
|
||||||
$tile->setRotation($this->rotation);
|
$tile->setType($this->type);
|
||||||
$tile->setType($this->type);
|
$this->level->addTile($tile);
|
||||||
}
|
|
||||||
$this->level->addTile($tile);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getHardness() : float{
|
public function getHardness() : float{
|
||||||
|
@ -84,17 +84,16 @@ class StandingBanner extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if($ret){
|
if($ret){
|
||||||
$tile = Tile::createFromItem(Tile::BANNER, $this->getLevel(), $this->asVector3(), $item);
|
/** @var TileBanner $tile */
|
||||||
if($tile !== null){
|
$tile = Tile::createFromItem(TileBanner::class, $this->getLevel(), $this->asVector3(), $item);
|
||||||
if($tile instanceof TileBanner and $item instanceof ItemBanner){
|
if($item instanceof ItemBanner){
|
||||||
$tile->setBaseColor($item->getBaseColor());
|
$tile->setBaseColor($item->getBaseColor());
|
||||||
if(($patterns = $item->getPatterns()) !== null){
|
if(($patterns = $item->getPatterns()) !== null){
|
||||||
$tile->setPatterns($patterns);
|
$tile->setPatterns($patterns);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->level->addTile($tile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->level->addTile($tile);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,10 +125,6 @@ class Sign extends Spawnable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function updateCompoundTag(CompoundTag $nbt, Player $player) : bool{
|
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)){
|
if($nbt->hasTag(self::TAG_TEXT_BLOB, StringTag::class)){
|
||||||
$lines = array_pad(explode("\n", $nbt->getString(self::TAG_TEXT_BLOB)), 4, "");
|
$lines = array_pad(explode("\n", $nbt->getString(self::TAG_TEXT_BLOB)), 4, "");
|
||||||
}else{
|
}else{
|
||||||
|
@ -44,24 +44,14 @@ abstract class Tile extends Position{
|
|||||||
public const TAG_Y = "y";
|
public const TAG_Y = "y";
|
||||||
public const TAG_Z = "z";
|
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 */
|
/** @var string[] classes that extend Tile */
|
||||||
private static $knownTiles = [];
|
private static $knownTiles = [];
|
||||||
/** @var string[][] */
|
/** @var string[][] */
|
||||||
private static $saveNames = [];
|
private static $saveNames = [];
|
||||||
|
|
||||||
|
/** @var string[] base class => overridden class */
|
||||||
|
private static $classMapping = [];
|
||||||
|
|
||||||
/** @var string */
|
/** @var string */
|
||||||
public $name = "";
|
public $name = "";
|
||||||
/** @var bool */
|
/** @var bool */
|
||||||
@ -70,16 +60,16 @@ abstract class Tile extends Position{
|
|||||||
protected $timings;
|
protected $timings;
|
||||||
|
|
||||||
public static function init(){
|
public static function init(){
|
||||||
self::registerTile(Banner::class, [self::BANNER, "minecraft:banner"]);
|
self::register(Banner::class, ["Banner", "minecraft:banner"]);
|
||||||
self::registerTile(Bed::class, [self::BED, "minecraft:bed"]);
|
self::register(Bed::class, ["Bed", "minecraft:bed"]);
|
||||||
self::registerTile(Chest::class, [self::CHEST, "minecraft:chest"]);
|
self::register(Chest::class, ["Chest", "minecraft:chest"]);
|
||||||
self::registerTile(EnchantTable::class, [self::ENCHANT_TABLE, "minecraft:enchanting_table"]);
|
self::register(EnchantTable::class, ["EnchantTable", "minecraft:enchanting_table"]);
|
||||||
self::registerTile(EnderChest::class, [self::ENDER_CHEST, "minecraft:ender_chest"]);
|
self::register(EnderChest::class, ["EnderChest", "minecraft:ender_chest"]);
|
||||||
self::registerTile(FlowerPot::class, [self::FLOWER_POT, "minecraft:flower_pot"]);
|
self::register(FlowerPot::class, ["FlowerPot", "minecraft:flower_pot"]);
|
||||||
self::registerTile(Furnace::class, [self::FURNACE, "minecraft:furnace"]);
|
self::register(Furnace::class, ["Furnace", "minecraft:furnace"]);
|
||||||
self::registerTile(ItemFrame::class, [self::ITEM_FRAME]); //this is an entity in PC
|
self::register(ItemFrame::class, ["ItemFrame"]); //this is an entity in PC
|
||||||
self::registerTile(Sign::class, [self::SIGN, "minecraft:sign"]);
|
self::register(Sign::class, ["Sign", "minecraft:sign"]);
|
||||||
self::registerTile(Skull::class, [self::SKULL, "minecraft:skull"]);
|
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{
|
public static function createFromData(Level $level, CompoundTag $nbt) : ?Tile{
|
||||||
$type = $nbt->getString(self::TAG_ID, "", true);
|
$type = $nbt->getString(self::TAG_ID, "", true);
|
||||||
if($type === ""){
|
if(!isset(self::$knownTiles[$type])){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$tile = self::create($type, $level, new Vector3($nbt->getInt(self::TAG_X), $nbt->getInt(self::TAG_Y), $nbt->getInt(self::TAG_Z)));
|
$class = self::$knownTiles[$type];
|
||||||
if($tile !== null){
|
assert(is_a($class, Tile::class, true));
|
||||||
$tile->readSaveData($nbt);
|
/**
|
||||||
}
|
* @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;
|
return $tile;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $type
|
* @param string $baseClass
|
||||||
* @param Level $level
|
* @param Level $level
|
||||||
* @param Vector3 $pos
|
* @param Vector3 $pos
|
||||||
* @param Item $item
|
* @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{
|
public static function createFromItem(string $baseClass, Level $level, Vector3 $pos, Item $item) : Tile{
|
||||||
$tile = self::create($type, $level, $pos);
|
$tile = self::create($baseClass, $level, $pos);
|
||||||
if($tile !== null and $item->hasCustomBlockData()){
|
if($item->hasCustomBlockData()){
|
||||||
$tile->readSaveData($item->getCustomBlockData());
|
$tile->readSaveData($item->getCustomBlockData());
|
||||||
}
|
}
|
||||||
if($tile instanceof Nameable and $item->hasCustomName()){ //this should take precedence over saved NBT
|
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;
|
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 $className
|
||||||
* @param string[] $saveNames
|
* @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);
|
Utils::testValidInstance($className, Tile::class);
|
||||||
|
|
||||||
|
self::$classMapping[$className] = $className;
|
||||||
|
|
||||||
$shortName = (new \ReflectionClass($className))->getShortName();
|
$shortName = (new \ReflectionClass($className))->getShortName();
|
||||||
if(!in_array($shortName, $saveNames, true)){
|
if(!in_array($shortName, $saveNames, true)){
|
||||||
$saveNames[] = $shortName;
|
$saveNames[] = $shortName;
|
||||||
@ -160,6 +136,44 @@ abstract class Tile extends Position{
|
|||||||
self::$saveNames[$className] = $saveNames;
|
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
|
* Returns the short save name
|
||||||
* @return string
|
* @return string
|
||||||
|
Loading…
x
Reference in New Issue
Block a user