diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 9126a382cd..cd3b92920b 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -101,7 +101,7 @@ use pocketmine\scheduler\AsyncPool; use pocketmine\scheduler\SendUsageTask; use pocketmine\snooze\SleeperHandler; use pocketmine\snooze\SleeperNotifier; -use pocketmine\tile\Tile; +use pocketmine\tile\TileFactory; use pocketmine\timings\Timings; use pocketmine\timings\TimingsHandler; use pocketmine\updater\AutoUpdater; @@ -1697,7 +1697,7 @@ class Server{ $this->commandMap = new SimpleCommandMap($this); EntityFactory::init(); - Tile::init(); + TileFactory::init(); BlockFactory::init(); BlockFactory::registerStaticRuntimeIdMappings(); Enchantment::init(); diff --git a/src/pocketmine/block/Bed.php b/src/pocketmine/block/Bed.php index edfc72ed85..3a63b790c5 100644 --- a/src/pocketmine/block/Bed.php +++ b/src/pocketmine/block/Bed.php @@ -35,7 +35,7 @@ use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\Player; use pocketmine\tile\Bed as TileBed; -use pocketmine\tile\Tile; +use pocketmine\tile\TileFactory; use pocketmine\utils\TextFormat; class Bed extends Transparent{ @@ -88,7 +88,7 @@ class Bed extends Transparent{ parent::writeStateToWorld(); //extra block properties storage hack /** @var TileBed $tile */ - $tile = Tile::create(TileBed::class, $this->getLevel(), $this->asVector3()); + $tile = TileFactory::create(TileBed::class, $this->getLevel(), $this->asVector3()); $tile->setColor($this->color); $this->level->addTile($tile); } diff --git a/src/pocketmine/block/Chest.php b/src/pocketmine/block/Chest.php index 9e0beb7e26..a7bdc97509 100644 --- a/src/pocketmine/block/Chest.php +++ b/src/pocketmine/block/Chest.php @@ -30,7 +30,7 @@ use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\Player; use pocketmine\tile\Chest as TileChest; -use pocketmine\tile\Tile; +use pocketmine\tile\TileFactory; class Chest extends Transparent{ @@ -95,7 +95,7 @@ class Chest extends Transparent{ if(parent::place($item, $blockReplace, $blockClicked, $face, $clickVector, $player)){ /** @var TileChest $tile */ - $tile = Tile::createFromItem(TileChest::class, $this->getLevel(), $this->asVector3(), $item); + $tile = TileFactory::createFromItem(TileChest::class, $this->getLevel(), $this->asVector3(), $item); $this->level->addTile($tile); if($pair instanceof TileChest){ diff --git a/src/pocketmine/block/EnchantingTable.php b/src/pocketmine/block/EnchantingTable.php index 86a88a8c31..5a1a628634 100644 --- a/src/pocketmine/block/EnchantingTable.php +++ b/src/pocketmine/block/EnchantingTable.php @@ -31,7 +31,7 @@ use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\Player; use pocketmine\tile\EnchantTable as TileEnchantingTable; -use pocketmine\tile\Tile; +use pocketmine\tile\TileFactory; class EnchantingTable extends Transparent{ @@ -43,7 +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)){ - $this->level->addTile(Tile::createFromItem(TileEnchantingTable::class, $this->getLevel(), $this->asVector3(), $item)); + $this->level->addTile(TileFactory::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 e8026a5aff..1bde46ab26 100644 --- a/src/pocketmine/block/EnderChest.php +++ b/src/pocketmine/block/EnderChest.php @@ -30,7 +30,7 @@ use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\Player; use pocketmine\tile\EnderChest as TileEnderChest; -use pocketmine\tile\Tile; +use pocketmine\tile\TileFactory; class EnderChest extends Chest{ @@ -66,7 +66,7 @@ class EnderChest extends Chest{ } if(Block::place($item, $blockReplace, $blockClicked, $face, $clickVector, $player)){ - $this->level->addTile(Tile::createFromItem(TileEnderChest::class, $this->getLevel(), $this->asVector3(), $item)); + $this->level->addTile(TileFactory::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 f9ac6eb609..4b24d626a7 100644 --- a/src/pocketmine/block/FlowerPot.php +++ b/src/pocketmine/block/FlowerPot.php @@ -29,7 +29,7 @@ use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\Player; use pocketmine\tile\FlowerPot as TileFlowerPot; -use pocketmine\tile\Tile; +use pocketmine\tile\TileFactory; class FlowerPot extends Flowable{ @@ -69,7 +69,7 @@ class FlowerPot extends Flowable{ } if(parent::place($item, $blockReplace, $blockClicked, $face, $clickVector, $player)){ - $this->level->addTile(Tile::createFromItem(TileFlowerPot::class, $this->getLevel(), $this->asVector3(), $item)); + $this->level->addTile(TileFactory::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 0e2dc49e97..031accfad4 100644 --- a/src/pocketmine/block/Furnace.php +++ b/src/pocketmine/block/Furnace.php @@ -30,7 +30,7 @@ use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\Player; use pocketmine\tile\Furnace as TileFurnace; -use pocketmine\tile\Tile; +use pocketmine\tile\TileFactory; class Furnace extends Solid{ @@ -100,7 +100,7 @@ class Furnace extends Solid{ $this->facing = Facing::opposite($player->getHorizontalFacing()); } if(parent::place($item, $blockReplace, $blockClicked, $face, $clickVector, $player)){ - $this->level->addTile(Tile::createFromItem(TileFurnace::class, $this->getLevel(), $this->asVector3(), $item)); + $this->level->addTile(TileFactory::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 40b9930bad..827d7c2ba1 100644 --- a/src/pocketmine/block/ItemFrame.php +++ b/src/pocketmine/block/ItemFrame.php @@ -29,7 +29,7 @@ use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\Player; use pocketmine\tile\ItemFrame as TileItemFrame; -use pocketmine\tile\Tile; +use pocketmine\tile\TileFactory; use function lcg_value; class ItemFrame extends Flowable{ @@ -87,7 +87,7 @@ class ItemFrame extends Flowable{ $this->facing = $face; if(parent::place($item, $blockReplace, $blockClicked, $face, $clickVector, $player)){ - $this->level->addTile(Tile::createFromItem(TileItemFrame::class, $this->getLevel(), $this->asVector3(), $item)); + $this->level->addTile(TileFactory::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 4531b4f8c7..d9d05131b6 100644 --- a/src/pocketmine/block/SignPost.php +++ b/src/pocketmine/block/SignPost.php @@ -29,7 +29,7 @@ use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\Player; use pocketmine\tile\Sign as TileSign; -use pocketmine\tile\Tile; +use pocketmine\tile\TileFactory; use function floor; class SignPost extends Transparent{ @@ -84,7 +84,7 @@ class SignPost extends Transparent{ } if($ret){ - $this->level->addTile(Tile::createFromItem(TileSign::class, $this->getLevel(), $this->asVector3(), $item)); + $this->level->addTile(TileFactory::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 5208f7e0db..156ed0da7b 100644 --- a/src/pocketmine/block/Skull.php +++ b/src/pocketmine/block/Skull.php @@ -31,7 +31,7 @@ use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\Player; use pocketmine\tile\Skull as TileSkull; -use pocketmine\tile\Tile; +use pocketmine\tile\TileFactory; use function floor; class Skull extends Flowable{ @@ -73,7 +73,7 @@ class Skull extends Flowable{ public function writeStateToWorld() : void{ parent::writeStateToWorld(); /** @var TileSkull $tile */ - $tile = Tile::create(TileSkull::class, $this->getLevel(), $this->asVector3()); + $tile = TileFactory::create(TileSkull::class, $this->getLevel(), $this->asVector3()); $tile->setRotation($this->rotation); $tile->setType($this->type); $this->level->addTile($tile); diff --git a/src/pocketmine/block/StandingBanner.php b/src/pocketmine/block/StandingBanner.php index 7a011c68fa..ff58a176e6 100644 --- a/src/pocketmine/block/StandingBanner.php +++ b/src/pocketmine/block/StandingBanner.php @@ -31,7 +31,7 @@ use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\Player; use pocketmine\tile\Banner as TileBanner; -use pocketmine\tile\Tile; +use pocketmine\tile\TileFactory; use function floor; class StandingBanner extends Transparent{ @@ -85,7 +85,7 @@ class StandingBanner extends Transparent{ } if($ret){ - $this->level->addTile(Tile::createFromItem(TileBanner::class, $this->getLevel(), $this->asVector3(), $item)); + $this->level->addTile(TileFactory::createFromItem(TileBanner::class, $this->getLevel(), $this->asVector3(), $item)); return true; } } diff --git a/src/pocketmine/level/format/Chunk.php b/src/pocketmine/level/format/Chunk.php index bba26c00a1..38799b0601 100644 --- a/src/pocketmine/level/format/Chunk.php +++ b/src/pocketmine/level/format/Chunk.php @@ -34,6 +34,7 @@ use pocketmine\nbt\tag\CompoundTag; use pocketmine\Player; use pocketmine\tile\Spawnable; use pocketmine\tile\Tile; +use pocketmine\tile\TileFactory; use pocketmine\utils\BinaryStream; use function array_fill; use function array_filter; @@ -619,7 +620,7 @@ class Chunk{ $level->timings->syncChunkLoadTileEntitiesTimer->startTiming(); foreach($this->NBTtiles as $nbt){ if($nbt instanceof CompoundTag){ - if(($tile = Tile::createFromData($level, $nbt)) !== null){ + if(($tile = TileFactory::createFromData($level, $nbt)) !== null){ $level->addTile($tile); }else{ $changed = true; diff --git a/src/pocketmine/tile/Banner.php b/src/pocketmine/tile/Banner.php index f63110baac..3266236fb7 100644 --- a/src/pocketmine/tile/Banner.php +++ b/src/pocketmine/tile/Banner.php @@ -113,7 +113,7 @@ class Banner extends Spawnable implements Nameable{ parent::__construct($level, $pos); } - protected function readSaveData(CompoundTag $nbt) : void{ + public function readSaveData(CompoundTag $nbt) : void{ $this->baseColor = $nbt->getInt(self::TAG_BASE, $this->baseColor, true); $this->patterns = $nbt->getListTag(self::TAG_PATTERNS) ?? $this->patterns; $this->loadName($nbt); @@ -131,7 +131,7 @@ class Banner extends Spawnable implements Nameable{ $this->addNameSpawnData($nbt); } - protected function copyDataFromItem(Item $item) : void{ + public function copyDataFromItem(Item $item) : void{ parent::copyDataFromItem($item); $this->copyNameFromItem($item); if($item instanceof ItemBanner){ diff --git a/src/pocketmine/tile/Bed.php b/src/pocketmine/tile/Bed.php index ebcd291f70..42fe1c776f 100644 --- a/src/pocketmine/tile/Bed.php +++ b/src/pocketmine/tile/Bed.php @@ -39,7 +39,7 @@ class Bed extends Spawnable{ $this->onChanged(); } - protected function readSaveData(CompoundTag $nbt) : void{ + public function readSaveData(CompoundTag $nbt) : void{ $this->color = $nbt->getByte(self::TAG_COLOR, 14, true); } diff --git a/src/pocketmine/tile/Chest.php b/src/pocketmine/tile/Chest.php index e9734cabf5..a5aad2fdab 100644 --- a/src/pocketmine/tile/Chest.php +++ b/src/pocketmine/tile/Chest.php @@ -56,7 +56,7 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{ parent::__construct($level, $pos); } - protected function readSaveData(CompoundTag $nbt) : void{ + public function readSaveData(CompoundTag $nbt) : void{ if($nbt->hasTag(self::TAG_PAIRX, IntTag::class) and $nbt->hasTag(self::TAG_PAIRZ, IntTag::class)){ $this->pairX = $nbt->getInt(self::TAG_PAIRX); $this->pairZ = $nbt->getInt(self::TAG_PAIRZ); diff --git a/src/pocketmine/tile/EnchantTable.php b/src/pocketmine/tile/EnchantTable.php index e2618a8ff8..d6dc08a20b 100644 --- a/src/pocketmine/tile/EnchantTable.php +++ b/src/pocketmine/tile/EnchantTable.php @@ -25,7 +25,7 @@ namespace pocketmine\tile; class EnchantTable extends Spawnable implements Nameable{ use NameableTrait{ - loadName as readSaveData; + loadName as public readSaveData; saveName as writeSaveData; } diff --git a/src/pocketmine/tile/EnderChest.php b/src/pocketmine/tile/EnderChest.php index 5faa2b2929..9493c924e6 100644 --- a/src/pocketmine/tile/EnderChest.php +++ b/src/pocketmine/tile/EnderChest.php @@ -27,7 +27,7 @@ use pocketmine\nbt\tag\CompoundTag; class EnderChest extends Spawnable{ - protected function readSaveData(CompoundTag $nbt) : void{ + public function readSaveData(CompoundTag $nbt) : void{ } diff --git a/src/pocketmine/tile/FlowerPot.php b/src/pocketmine/tile/FlowerPot.php index 7ee5c7e681..b8aefefc6c 100644 --- a/src/pocketmine/tile/FlowerPot.php +++ b/src/pocketmine/tile/FlowerPot.php @@ -43,7 +43,7 @@ class FlowerPot extends Spawnable{ parent::__construct($level, $pos); } - protected function readSaveData(CompoundTag $nbt) : void{ + public function readSaveData(CompoundTag $nbt) : void{ if($nbt->hasTag(self::TAG_ITEM, ShortTag::class) and $nbt->hasTag(self::TAG_ITEM_DATA, IntTag::class)){ $this->item = ItemFactory::get($nbt->getShort(self::TAG_ITEM, 0), $nbt->getInt(self::TAG_ITEM_DATA, 0), 1); } diff --git a/src/pocketmine/tile/Furnace.php b/src/pocketmine/tile/Furnace.php index 814543557c..b98dd5b551 100644 --- a/src/pocketmine/tile/Furnace.php +++ b/src/pocketmine/tile/Furnace.php @@ -68,7 +68,7 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{ parent::__construct($level, $pos); } - protected function readSaveData(CompoundTag $nbt) : void{ + public function readSaveData(CompoundTag $nbt) : void{ $this->burnTime = max(0, $nbt->getShort(self::TAG_BURN_TIME, $this->burnTime, true)); $this->cookTime = $nbt->getShort(self::TAG_COOK_TIME, $this->cookTime, true); diff --git a/src/pocketmine/tile/ItemFrame.php b/src/pocketmine/tile/ItemFrame.php index 2fbb1f4b5c..a4c5108171 100644 --- a/src/pocketmine/tile/ItemFrame.php +++ b/src/pocketmine/tile/ItemFrame.php @@ -46,7 +46,7 @@ class ItemFrame extends Spawnable{ parent::__construct($level, $pos); } - protected function readSaveData(CompoundTag $nbt) : void{ + public function readSaveData(CompoundTag $nbt) : void{ if(($itemTag = $nbt->getCompoundTag(self::TAG_ITEM)) !== null){ $this->item = Item::nbtDeserialize($itemTag); } diff --git a/src/pocketmine/tile/NameableTrait.php b/src/pocketmine/tile/NameableTrait.php index 4b159df422..8f9ad1070b 100644 --- a/src/pocketmine/tile/NameableTrait.php +++ b/src/pocketmine/tile/NameableTrait.php @@ -86,7 +86,7 @@ trait NameableTrait{ * @param Item $item * @see Tile::copyDataFromItem() */ - protected function copyDataFromItem(Item $item) : void{ + public function copyDataFromItem(Item $item) : void{ parent::copyDataFromItem($item); if($item->hasCustomName()){ //this should take precedence over saved NBT $this->setName($item->getCustomName()); diff --git a/src/pocketmine/tile/Sign.php b/src/pocketmine/tile/Sign.php index 1f5ef98a2e..55d64f1c37 100644 --- a/src/pocketmine/tile/Sign.php +++ b/src/pocketmine/tile/Sign.php @@ -44,7 +44,7 @@ class Sign extends Spawnable{ /** @var string[] */ protected $text = ["", "", "", ""]; - protected function readSaveData(CompoundTag $nbt) : void{ + public function readSaveData(CompoundTag $nbt) : void{ if($nbt->hasTag(self::TAG_TEXT_BLOB, StringTag::class)){ //MCPE 1.2 save format $this->text = array_pad(explode("\n", $nbt->getString(self::TAG_TEXT_BLOB)), 4, ""); assert(count($this->text) === 4, "Too many lines!"); diff --git a/src/pocketmine/tile/Skull.php b/src/pocketmine/tile/Skull.php index 1421fe5db2..87876a76c7 100644 --- a/src/pocketmine/tile/Skull.php +++ b/src/pocketmine/tile/Skull.php @@ -43,7 +43,7 @@ class Skull extends Spawnable{ /** @var int */ private $skullRotation = 0; - protected function readSaveData(CompoundTag $nbt) : void{ + public function readSaveData(CompoundTag $nbt) : void{ $this->skullType = $nbt->getByte(self::TAG_SKULL_TYPE, $this->skullType, true); $this->skullRotation = $nbt->getByte(self::TAG_ROT, $this->skullRotation, true); } diff --git a/src/pocketmine/tile/Spawnable.php b/src/pocketmine/tile/Spawnable.php index 0c8200241a..b4954ffb1c 100644 --- a/src/pocketmine/tile/Spawnable.php +++ b/src/pocketmine/tile/Spawnable.php @@ -29,6 +29,7 @@ use pocketmine\nbt\tag\StringTag; use pocketmine\network\mcpe\NetworkLittleEndianNBTStream; use pocketmine\network\mcpe\protocol\BlockEntityDataPacket; use pocketmine\Player; +use function get_class; abstract class Spawnable extends Tile{ /** @var string|null */ @@ -108,7 +109,7 @@ abstract class Spawnable extends Tile{ */ final public function getSpawnCompound() : CompoundTag{ $nbt = new CompoundTag("", [ - new StringTag(self::TAG_ID, static::getSaveId()), + new StringTag(self::TAG_ID, TileFactory::getSaveId(get_class($this))), //TODO: disassociate network ID from save ID new IntTag(self::TAG_X, $this->x), new IntTag(self::TAG_Y, $this->y), new IntTag(self::TAG_Z, $this->z) diff --git a/src/pocketmine/tile/Tile.php b/src/pocketmine/tile/Tile.php index 86a6e9dd1c..60bb22fff5 100644 --- a/src/pocketmine/tile/Tile.php +++ b/src/pocketmine/tile/Tile.php @@ -35,13 +35,7 @@ use pocketmine\math\Vector3; use pocketmine\nbt\tag\CompoundTag; use pocketmine\timings\Timings; use pocketmine\timings\TimingsHandler; -use pocketmine\utils\Utils; -use function assert; -use function current; use function get_class; -use function in_array; -use function is_a; -use function reset; abstract class Tile extends Position{ @@ -50,14 +44,6 @@ abstract class Tile extends Position{ public const TAG_Y = "y"; public const TAG_Z = "z"; - /** @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 */ @@ -65,140 +51,18 @@ abstract class Tile extends Position{ /** @var TimingsHandler */ protected $timings; - public static function init(){ - 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"]); - } - - /** - * @param Level $level - * @param CompoundTag $nbt - * - * @return Tile|null - */ - public static function createFromData(Level $level, CompoundTag $nbt) : ?Tile{ - $type = $nbt->getString(self::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($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 $baseClass - * @param Level $level - * @param Vector3 $pos - * @param Item $item - * - * @return Tile (instanceof $baseClass) - * @throws \InvalidArgumentException if the base class is not a registered tile - */ - public static function createFromItem(string $baseClass, Level $level, Vector3 $pos, Item $item) : Tile{ - $tile = self::create($baseClass, $level, $pos); - $tile->copyDataFromItem($item); - - return $tile; - } - - /** - * @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 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 - */ - public static function getSaveId() : string{ - if(!isset(self::$saveNames[static::class])){ - throw new \InvalidStateException("Tile is not registered"); - } - - reset(self::$saveNames[static::class]); - return current(self::$saveNames[static::class]); - } - public function __construct(Level $level, Vector3 $pos){ $this->timings = Timings::getTileEntityTimings($this); parent::__construct($pos->getFloorX(), $pos->getFloorY(), $pos->getFloorZ(), $level); } /** + * @internal * Reads additional data from the CompoundTag on tile creation. * * @param CompoundTag $nbt */ - abstract protected function readSaveData(CompoundTag $nbt) : void; + abstract public function readSaveData(CompoundTag $nbt) : void; /** * Writes additional save data to a CompoundTag, not including generic things like ID and coordinates. @@ -209,7 +73,7 @@ abstract class Tile extends Position{ public function saveNBT() : CompoundTag{ $nbt = new CompoundTag(); - $nbt->setString(self::TAG_ID, static::getSaveId()); + $nbt->setString(self::TAG_ID, TileFactory::getSaveId(get_class($this))); $nbt->setInt(self::TAG_X, $this->x); $nbt->setInt(self::TAG_Y, $this->y); $nbt->setInt(self::TAG_Z, $this->z); @@ -223,7 +87,14 @@ abstract class Tile extends Position{ return $tag->getCount() > 0 ? $tag : null; } - protected function copyDataFromItem(Item $item) : void{ + /** + * @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()); } diff --git a/src/pocketmine/tile/TileFactory.php b/src/pocketmine/tile/TileFactory.php new file mode 100644 index 0000000000..7073b619af --- /dev/null +++ b/src/pocketmine/tile/TileFactory.php @@ -0,0 +1,171 @@ + overridden class */ + private static $classMapping = []; + + private function __construct(){ + //NOOP + } + + public static function init(){ + 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"]); + } + + /** + * @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 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"); + } + + /** + * @internal + * + * @param string $baseClass + * @param Level $level + * @param Vector3 $pos + * @param Item $item + * + * @return Tile (instanceof $baseClass) + * @throws \InvalidArgumentException if the base class is not a registered tile + */ + public static function createFromItem(string $baseClass, Level $level, Vector3 $pos, Item $item) : Tile{ + $tile = self::create($baseClass, $level, $pos); + $tile->copyDataFromItem($item); + + return $tile; + } + + /** + * @internal + * + * @param Level $level + * @param CompoundTag $nbt + * + * @return Tile|null + */ + public static function createFromData(Level $level, 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($level, 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"); + } +}