mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-05-27 23:57:02 +00:00
This is better for performance because these then don't need to be reevaluated every time they are called. When encountering an unqualified function or constant reference, PHP will first try to locate a symbol in the current namespace by that name, and then fall back to the global namespace. This short-circuits the check, which has substantial performance effects in some cases - in particular, ord(), chr() and strlen() show ~1500x faster calls when they are fully qualified. However, this doesn't mean that PM is getting a massive amount faster. In real world terms, this translates to about 10-15% performance improvement. But before anyone gets excited, you should know that the CodeOptimizer in the PreProcessor repo has been applying fully-qualified symbol optimizations to Jenkins builds for years, which is one of the reasons why Jenkins builds have better performance than home-built or source installations. We're choosing to do this for the sake of future SafePHP integration and also to be able to get rid of the buggy CodeOptimizer, so that phar and source are more consistent.
298 lines
7.8 KiB
PHP
298 lines
7.8 KiB
PHP
<?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\tile;
|
|
|
|
use pocketmine\block\Block;
|
|
use pocketmine\item\Item;
|
|
use pocketmine\level\Level;
|
|
use pocketmine\level\Position;
|
|
use pocketmine\math\Vector3;
|
|
use pocketmine\nbt\tag\CompoundTag;
|
|
use pocketmine\nbt\tag\IntTag;
|
|
use pocketmine\nbt\tag\StringTag;
|
|
use pocketmine\Player;
|
|
use pocketmine\Server;
|
|
use pocketmine\timings\Timings;
|
|
use pocketmine\timings\TimingsHandler;
|
|
use function current;
|
|
use function get_class;
|
|
use function in_array;
|
|
use function is_a;
|
|
use function reset;
|
|
|
|
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";
|
|
|
|
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 int */
|
|
public static $tileCount = 1;
|
|
|
|
/** @var string[] classes that extend Tile */
|
|
private static $knownTiles = [];
|
|
/** @var string[][] */
|
|
private static $saveNames = [];
|
|
|
|
/** @var string */
|
|
public $name;
|
|
/** @var int */
|
|
public $id;
|
|
/** @var bool */
|
|
public $closed = false;
|
|
/** @var Server */
|
|
protected $server;
|
|
/** @var TimingsHandler */
|
|
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"]);
|
|
}
|
|
|
|
/**
|
|
* @param string $type
|
|
* @param Level $level
|
|
* @param CompoundTag $nbt
|
|
* @param mixed ...$args
|
|
*
|
|
* @return Tile|null
|
|
*/
|
|
public static function createTile($type, Level $level, CompoundTag $nbt, ...$args) : ?Tile{
|
|
if(isset(self::$knownTiles[$type])){
|
|
$class = self::$knownTiles[$type];
|
|
/** @see Tile::__construct() */
|
|
return new $class($level, $nbt, ...$args);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @param string $className
|
|
* @param array $saveNames
|
|
*
|
|
* @return bool
|
|
* @throws \ReflectionException
|
|
*/
|
|
public static function registerTile(string $className, array $saveNames = []) : bool{
|
|
$class = new \ReflectionClass($className);
|
|
if(is_a($className, Tile::class, true) and !$class->isAbstract()){
|
|
$shortName = $class->getShortName();
|
|
if(!in_array($shortName, $saveNames, true)){
|
|
$saveNames[] = $shortName;
|
|
}
|
|
|
|
foreach($saveNames as $name){
|
|
self::$knownTiles[$name] = $className;
|
|
}
|
|
|
|
self::$saveNames[$className] = $saveNames;
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* 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, CompoundTag $nbt){
|
|
$this->timings = Timings::getTileEntityTimings($this);
|
|
|
|
$this->server = $level->getServer();
|
|
$this->name = "";
|
|
$this->id = Tile::$tileCount++;
|
|
|
|
parent::__construct($nbt->getInt(self::TAG_X), $nbt->getInt(self::TAG_Y), $nbt->getInt(self::TAG_Z), $level);
|
|
$this->readSaveData($nbt);
|
|
|
|
$this->getLevel()->addTile($this);
|
|
}
|
|
|
|
public function getId() : int{
|
|
return $this->id;
|
|
}
|
|
|
|
/**
|
|
* Reads additional data from the CompoundTag on tile creation.
|
|
*
|
|
* @param CompoundTag $nbt
|
|
*/
|
|
abstract protected 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 = new CompoundTag();
|
|
$nbt->setString(self::TAG_ID, static::getSaveId());
|
|
$nbt->setInt(self::TAG_X, $this->x);
|
|
$nbt->setInt(self::TAG_Y, $this->y);
|
|
$nbt->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;
|
|
}
|
|
|
|
/**
|
|
* Creates and returns a CompoundTag containing the necessary information to spawn a tile of this type.
|
|
*
|
|
* @param Vector3 $pos
|
|
* @param int|null $face
|
|
* @param Item|null $item
|
|
* @param Player|null $player
|
|
*
|
|
* @return CompoundTag
|
|
*/
|
|
public static function createNBT(Vector3 $pos, ?int $face = null, ?Item $item = null, ?Player $player = null) : CompoundTag{
|
|
if(static::class === self::class){
|
|
throw new \BadMethodCallException(__METHOD__ . " must be called from the scope of a child class");
|
|
}
|
|
$nbt = new CompoundTag("", [
|
|
new StringTag(self::TAG_ID, static::getSaveId()),
|
|
new IntTag(self::TAG_X, (int) $pos->x),
|
|
new IntTag(self::TAG_Y, (int) $pos->y),
|
|
new IntTag(self::TAG_Z, (int) $pos->z)
|
|
]);
|
|
|
|
static::createAdditionalNBT($nbt, $pos, $face, $item, $player);
|
|
|
|
if($item !== null){
|
|
$customBlockData = $item->getCustomBlockData();
|
|
if($customBlockData !== null){
|
|
foreach($customBlockData as $customBlockDataTag){
|
|
$nbt->setTag(clone $customBlockDataTag);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $nbt;
|
|
}
|
|
|
|
/**
|
|
* Called by createNBT() to allow descendent classes to add their own base NBT using the parameters provided.
|
|
*
|
|
* @param CompoundTag $nbt
|
|
* @param Vector3 $pos
|
|
* @param int|null $face
|
|
* @param Item|null $item
|
|
* @param Player|null $player
|
|
*/
|
|
protected static function createAdditionalNBT(CompoundTag $nbt, Vector3 $pos, ?int $face = null, ?Item $item = null, ?Player $player = null) : void{
|
|
|
|
}
|
|
|
|
/**
|
|
* @return Block
|
|
*/
|
|
public function getBlock() : Block{
|
|
return $this->level->getBlockAt($this->x, $this->y, $this->z);
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
public function onUpdate() : bool{
|
|
return false;
|
|
}
|
|
|
|
final public function scheduleUpdate() : void{
|
|
if($this->closed){
|
|
throw new \InvalidStateException("Cannot schedule update on garbage tile " . get_class($this));
|
|
}
|
|
$this->level->updateTiles[$this->id] = $this;
|
|
}
|
|
|
|
public function isClosed() : bool{
|
|
return $this->closed;
|
|
}
|
|
|
|
public function __destruct(){
|
|
$this->close();
|
|
}
|
|
|
|
public function close() : void{
|
|
if(!$this->closed){
|
|
$this->closed = true;
|
|
|
|
if($this->isValid()){
|
|
$this->level->removeTile($this);
|
|
$this->setLevel(null);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function getName() : string{
|
|
return $this->name;
|
|
}
|
|
}
|