Revamp Sign API, flatten API into blocks

This commit is contained in:
Dylan K. Taylor 2019-02-28 17:10:37 +00:00
parent f680a239f7
commit 5cca4b5e31
6 changed files with 170 additions and 191 deletions

View File

@ -92,21 +92,17 @@ use pocketmine\level\Level;
use pocketmine\level\Position;
use pocketmine\math\Vector3;
use pocketmine\metadata\MetadataValue;
use pocketmine\nbt\NbtDataException;
use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\DoubleTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\network\BadPacketException;
use pocketmine\network\mcpe\CompressBatchPromise;
use pocketmine\network\mcpe\NetworkCipher;
use pocketmine\network\mcpe\NetworkNbtSerializer;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\ProcessLoginTask;
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
use pocketmine\network\mcpe\protocol\AnimatePacket;
use pocketmine\network\mcpe\protocol\AvailableCommandsPacket;
use pocketmine\network\mcpe\protocol\BlockEntityDataPacket;
use pocketmine\network\mcpe\protocol\BookEditPacket;
use pocketmine\network\mcpe\protocol\ChunkRadiusUpdatedPacket;
use pocketmine\network\mcpe\protocol\ClientboundPacket;
@ -134,7 +130,6 @@ use pocketmine\permission\PermissionAttachment;
use pocketmine\permission\PermissionAttachmentInfo;
use pocketmine\permission\PermissionManager;
use pocketmine\plugin\Plugin;
use pocketmine\tile\Spawnable;
use pocketmine\tile\Tile;
use pocketmine\timings\Timings;
use pocketmine\utils\TextFormat;
@ -2457,36 +2452,6 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener,
return $handled;
}
/**
* @param BlockEntityDataPacket $packet
*
* @return bool
* @throws BadPacketException
*/
public function handleBlockEntityData(BlockEntityDataPacket $packet) : bool{
$this->doCloseInventory();
$pos = new Vector3($packet->x, $packet->y, $packet->z);
if($pos->distanceSquared($this) > 10000){
return true;
}
$t = $this->level->getTile($pos);
if($t instanceof Spawnable){
$nbt = new NetworkNbtSerializer();
try{
$compound = $nbt->read($packet->namedtag);
}catch(NbtDataException $e){
throw new BadPacketException($e->getMessage(), 0, $e);
}
if(!$t->updateCompoundTag($compound, $this)){
$this->level->sendBlocks([$this], [$pos]);
}
}
return true;
}
public function handleBookEdit(BookEditPacket $packet) : bool{
/** @var WritableBook $oldBook */
$oldBook = $this->inventory->getItem($packet->inventorySlot);

View File

@ -23,11 +23,17 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\utils\SignText;
use pocketmine\event\block\SignChangeEvent;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\Player;
use pocketmine\tile\Sign as TileSign;
use pocketmine\utils\TextFormat;
use function array_map;
use function assert;
use function floor;
class SignPost extends Transparent{
@ -35,6 +41,14 @@ class SignPost extends Transparent{
/** @var int */
protected $rotation = 0;
/** @var SignText */
protected $text;
public function __construct(BlockIdentifier $idInfo, string $name){
parent::__construct($idInfo, $name);
$this->text = new SignText();
}
protected function writeStateToMeta() : int{
return $this->rotation;
}
@ -43,6 +57,21 @@ class SignPost extends Transparent{
$this->rotation = $stateMeta;
}
public function readStateFromWorld() : void{
parent::readStateFromWorld();
$tile = $this->level->getTile($this);
if($tile instanceof TileSign){
$this->text = $tile->getText();
}
}
public function writeStateToWorld() : void{
parent::writeStateToWorld();
$tile = $this->level->getTile($this);
assert($tile instanceof TileSign);
$tile->setText($this->text);
}
public function getStateBitmask() : int{
return 0b1111;
}
@ -82,4 +111,36 @@ class SignPost extends Transparent{
public function getToolType() : int{
return BlockToolType::TYPE_AXE;
}
/**
* Returns an object containing information about the sign text.
*
* @return SignText
*/
public function getText() : SignText{
return $this->text;
}
/**
* Called by the player controller (network session) to update the sign text, firing events as appropriate.
*
* @param Player $author
* @param SignText $text
*
* @return bool if the sign update was successful.
*/
public function updateText(Player $author, SignText $text) : bool{
$removeFormat = $author->getRemoveFormat();
$ev = new SignChangeEvent($this, $author, new SignText(array_map(function(string $line) use ($removeFormat){
return TextFormat::clean($line, $removeFormat);
}, $text->getLines())));
$ev->call();
if(!$ev->isCancelled()){
$this->text = clone $ev->getNewText();
$this->level->setBlock($this, $this);
return true;
}
return false;
}
}

View File

@ -23,32 +23,44 @@ declare(strict_types=1);
namespace pocketmine\event\block;
use pocketmine\block\Block;
use pocketmine\block\SignPost;
use pocketmine\block\utils\SignText;
use pocketmine\event\Cancellable;
use pocketmine\event\CancellableTrait;
use pocketmine\Player;
use function count;
/**
* Called when a sign is changed by a player.
* Called when a sign's text is changed by a player.
*/
class SignChangeEvent extends BlockEvent implements Cancellable{
use CancellableTrait;
/** @var SignPost */
private $sign;
/** @var Player */
private $player;
/** @var string[] */
private $lines = [];
/** @var SignText */
private $text;
/**
* @param Block $theBlock
* @param Player $thePlayer
* @param string[] $theLines
* @param SignPost $sign
* @param Player $player
* @param SignText $text
*/
public function __construct(Block $theBlock, Player $thePlayer, array $theLines){
parent::__construct($theBlock);
$this->player = $thePlayer;
$this->setLines($theLines);
public function __construct(SignPost $sign, Player $player, SignText $text){
parent::__construct($sign);
$this->sign = $sign;
$this->player = $player;
$this->text = $text;
}
/**
* @return SignPost
*/
public function getSign() : SignPost{
return $this->sign;
}
/**
@ -59,49 +71,29 @@ class SignChangeEvent extends BlockEvent implements Cancellable{
}
/**
* @return string[]
* Returns the text currently on the sign.
*
* @return SignText
*/
public function getLines() : array{
return $this->lines;
public function getOldText() : SignText{
return $this->sign->getText();
}
/**
* @param int $index 0-3
* Returns the text which will be on the sign after the event.
*
* @return string
*
* @throws \InvalidArgumentException if the index is out of bounds
* @return SignText
*/
public function getLine(int $index) : string{
if($index < 0 or $index > 3){
throw new \InvalidArgumentException("Index must be in the range 0-3!");
}
return $this->lines[$index];
public function getNewText() : SignText{
return $this->text;
}
/**
* @param string[] $lines
* Sets the text to be written on the sign after the event.
*
* @throws \InvalidArgumentException if there are more or less than 4 lines in the passed array
* @param SignText $text
*/
public function setLines(array $lines) : void{
if(count($lines) !== 4){
throw new \InvalidArgumentException("Array size must be 4!");
}
$this->lines = $lines;
}
/**
* @param int $index 0-3
* @param string $line
*
* @throws \InvalidArgumentException if the index is out of bounds
*/
public function setLine(int $index, string $line) : void{
if($index < 0 or $index > 3){
throw new \InvalidArgumentException("Index must be in the range 0-3!");
}
$this->lines[$index] = $line;
public function setNewText(SignText $text) : void{
$this->text = $text;
}
}

View File

@ -24,12 +24,17 @@ declare(strict_types=1);
namespace pocketmine\network\mcpe\handler;
use pocketmine\block\ItemFrame;
use pocketmine\block\SignPost;
use pocketmine\block\utils\SignText;
use pocketmine\inventory\transaction\action\InventoryAction;
use pocketmine\inventory\transaction\CraftingTransaction;
use pocketmine\inventory\transaction\InventoryTransaction;
use pocketmine\inventory\transaction\TransactionValidationException;
use pocketmine\math\Vector3;
use pocketmine\nbt\NbtDataException;
use pocketmine\nbt\tag\StringTag;
use pocketmine\network\BadPacketException;
use pocketmine\network\mcpe\NetworkNbtSerializer;
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
use pocketmine\network\mcpe\protocol\AnimatePacket;
use pocketmine\network\mcpe\protocol\BlockEntityDataPacket;
@ -72,6 +77,7 @@ use pocketmine\network\mcpe\protocol\types\ReleaseItemTransactionData;
use pocketmine\network\mcpe\protocol\types\UseItemOnEntityTransactionData;
use pocketmine\network\mcpe\protocol\types\UseItemTransactionData;
use pocketmine\Player;
use function base64_encode;
use function implode;
use function json_decode;
use function json_encode;
@ -385,7 +391,37 @@ class SimpleSessionHandler extends SessionHandler{
}
public function handleBlockEntityData(BlockEntityDataPacket $packet) : bool{
return $this->player->handleBlockEntityData($packet);
$pos = new Vector3($packet->x, $packet->y, $packet->z);
if($pos->distanceSquared($this->player) > 10000){
return false;
}
$block = $this->player->getLevel()->getBlock($pos);
try{
$nbt = (new NetworkNbtSerializer())->read($packet->namedtag);
}catch(NbtDataException $e){
throw new BadPacketException($e->getMessage(), 0, $e);
}
if($block instanceof SignPost){
if($nbt->hasTag("Text", StringTag::class)){
try{
$text = SignText::fromBlob($nbt->getString("Text"));
}catch(\InvalidArgumentException $e){
throw new BadPacketException("Invalid sign text update: " . $e->getMessage(), 0, $e);
}
if(!$block->updateText($this->player, $text)){
$this->player->getLevel()->sendBlocks([$this->player], [$block]);
}
return true;
}
$this->player->getServer()->getLogger()->debug("Invalid sign update data from " . $this->player->getName() . ": " . base64_encode($packet->namedtag));
}
return false;
}
public function handlePlayerInput(PlayerInputPacket $packet) : bool{

View File

@ -23,139 +23,78 @@ declare(strict_types=1);
namespace pocketmine\tile;
use pocketmine\event\block\SignChangeEvent;
use pocketmine\block\SignPost;
use pocketmine\block\utils\SignText;
use pocketmine\level\Level;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\Player;
use pocketmine\utils\TextFormat;
use function array_map;
use function array_pad;
use function array_slice;
use function explode;
use function implode;
use function mb_check_encoding;
use function mb_scrub;
use function sprintf;
/**
* @deprecated
* @see SignPost
*/
class Sign extends Spawnable{
public const TAG_TEXT_BLOB = "Text";
public const TAG_TEXT_LINE = "Text%d"; //sprintf()able
private static function fixTextBlob(string $blob) : array{
public static function fixTextBlob(string $blob) : array{
return array_slice(array_pad(explode("\n", $blob), 4, ""), 0, 4);
}
/** @var string[] */
protected $text = ["", "", "", ""];
/** @var SignText */
protected $text;
public function __construct(Level $level, Vector3 $pos){
$this->text = new SignText();
parent::__construct($level, $pos);
}
public function readSaveData(CompoundTag $nbt) : void{
if($nbt->hasTag(self::TAG_TEXT_BLOB, StringTag::class)){ //MCPE 1.2 save format
$this->text = self::fixTextBlob($nbt->getString(self::TAG_TEXT_BLOB));
$this->text = SignText::fromBlob(mb_scrub($nbt->getString(self::TAG_TEXT_BLOB), 'UTF-8'));
}else{
for($i = 1; $i <= 4; ++$i){
$textKey = sprintf(self::TAG_TEXT_LINE, $i);
$this->text = new SignText();
for($i = 0; $i < SignText::LINE_COUNT; ++$i){
$textKey = sprintf(self::TAG_TEXT_LINE, $i + 1);
if($nbt->hasTag($textKey, StringTag::class)){
$this->text[$i - 1] = $nbt->getString($textKey);
$this->text->setLine($i, mb_scrub($nbt->getString($textKey), 'UTF-8'));
}
}
}
$this->text = array_map(function(string $line) : string{
return mb_scrub($line, 'UTF-8');
}, $this->text);
}
protected function writeSaveData(CompoundTag $nbt) : void{
$nbt->setString(self::TAG_TEXT_BLOB, implode("\n", $this->text));
$nbt->setString(self::TAG_TEXT_BLOB, implode("\n", $this->text->getLines()));
for($i = 1; $i <= 4; ++$i){ //Backwards-compatibility
$textKey = sprintf(self::TAG_TEXT_LINE, $i);
$nbt->setString($textKey, $this->getLine($i - 1));
for($i = 0; $i < SignText::LINE_COUNT; ++$i){ //Backwards-compatibility
$textKey = sprintf(self::TAG_TEXT_LINE, $i + 1);
$nbt->setString($textKey, $this->text->getLine($i));
}
}
/**
* Changes contents of the specific lines to the string provided.
* Leaves contents of the specific lines as is if null is provided.
*
* @param null|string $line1
* @param null|string $line2
* @param null|string $line3
* @param null|string $line4
* @return SignText
*/
public function setText(?string $line1 = "", ?string $line2 = "", ?string $line3 = "", ?string $line4 = "") : void{
if($line1 !== null){
$this->setLine(0, $line1);
}
if($line2 !== null){
$this->setLine(1, $line2);
}
if($line3 !== null){
$this->setLine(2, $line3);
}
if($line4 !== null){
$this->setLine(3, $line4);
}
$this->onChanged();
}
/**
* @param int $index 0-3
* @param string $line
*/
public function setLine(int $index, string $line) : void{
if($index < 0 or $index > 3){
throw new \InvalidArgumentException("Index must be in the range 0-3!");
}
if(!mb_check_encoding($line, 'UTF-8')){
throw new \InvalidArgumentException("Text must be valid UTF-8");
}
$this->text[$index] = $line;
$this->onChanged();
}
/**
* @param int $index 0-3
*
* @return string
*/
public function getLine(int $index) : string{
if($index < 0 or $index > 3){
throw new \InvalidArgumentException("Index must be in the range 0-3!");
}
return $this->text[$index];
}
/**
* @return string[]
*/
public function getText() : array{
public function getText() : SignText{
return $this->text;
}
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
$nbt->setString(self::TAG_TEXT_BLOB, implode("\n", $this->text));
/**
* @param SignText $text
*/
public function setText(SignText $text) : void{
$this->text = $text;
$this->onChanged();
}
public function updateCompoundTag(CompoundTag $nbt, Player $player) : bool{
if($nbt->hasTag(self::TAG_TEXT_BLOB, StringTag::class)){
$lines = self::fixTextBlob($nbt->getString(self::TAG_TEXT_BLOB));
}else{
return false;
}
$removeFormat = $player->getRemoveFormat();
$ev = new SignChangeEvent($this->getBlock(), $player, array_map(function(string $line) use ($removeFormat){ return TextFormat::clean($line, $removeFormat); }, $lines));
$ev->call();
if(!$ev->isCancelled()){
$this->setText(...$ev->getLines());
return true;
}else{
return false;
}
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
$nbt->setString(self::TAG_TEXT_BLOB, implode("\n", $this->text->getLines()));
}
}

View File

@ -27,7 +27,6 @@ use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\network\mcpe\NetworkNbtSerializer;
use pocketmine\Player;
use function get_class;
abstract class Spawnable extends Tile{
@ -104,17 +103,4 @@ abstract class Spawnable extends Tile{
* @param CompoundTag $nbt
*/
abstract protected function addAdditionalSpawnData(CompoundTag $nbt) : void;
/**
* Called when a player updates a block entity's NBT data
* for example when writing on a sign.
*
* @param CompoundTag $nbt
* @param Player $player
*
* @return bool indication of success, will respawn the tile to the player if false.
*/
public function updateCompoundTag(CompoundTag $nbt, Player $player) : bool{
return false;
}
}