Merge branch 'stable' into next-minor

This commit is contained in:
Dylan K. Taylor 2020-02-27 16:51:06 +00:00
commit dbaf851be7
29 changed files with 228 additions and 42 deletions

@ -1 +1 @@
Subproject commit 83085714483a0cf3a13b4fa780bd14b153c5c36b
Subproject commit 61e20ab9e39fb64514c1031e5ae6320b8129e14c

@ -1 +1 @@
Subproject commit 63e0092d623d13e47f9083b3d65fdf431933a471
Subproject commit da363df5f15e3a92817683b695455eced04992ba

View File

@ -66,3 +66,16 @@ Plugin developers should **only** update their required API to this version if y
- `ClientboundMapItemDataPacket` now uses `MapDecoration` objects for decorations instead of associative arrays.
- Updated Composer dependencies to get bug fixes in `pocketmine/nbt` and other libraries.
- Packages `pocketmine/classloader` and `pocketmine/log` are now required; these provide classes previously part of `pocketmine/spl`. This change has no effect on API compatibility.
# 3.11.6
- Core code, tests and build scripts are now analyzed using `phpstan-strict-rules` and `phpstan-phpunit` rules.
- Added more PHPStan-specific type annotations to improve static analysis.
- Fixed more incorrect PHPDoc types.
- Added a workaround for player movement not working since 1.14.30.
- Fixed lava and water buckets being edible since 1.13.
- `AutoUpdater` is now created before any plugins are loaded.
- Fixed trees not generating below y=2 in custom generators.
- Fixed crash when opening a chest improperly unpaired from its pair (destroyed, setBlock(), unloaded, etc.).
- `ThreadManager` is now lazily initialized.
- Removed raw NBT storage from `Item` internals. The following methods are now deprecated:
- `Item::setCompoundTag()`

19
composer.lock generated
View File

@ -386,7 +386,7 @@
"source": {
"type": "git",
"url": "https://gitlab.irstea.fr/pole-is/tools/phpunit-shim.git",
"reference": "39b6155954d6caec1110a9e78582c4816ab247bc"
"reference": "8ec63f895972681271191821a36f9081c236b993"
},
"require": {
"ext-dom": "*",
@ -408,6 +408,11 @@
"phpunit"
],
"type": "library",
"autoload": {
"exclude-from-classmap": [
"phpunit"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
@ -427,20 +432,20 @@
"testing",
"xunit"
],
"time": "2020-01-09T03:20:20+00:00"
"time": "2020-01-23T13:39:47+00:00"
},
{
"name": "phpstan/phpstan",
"version": "0.12.9",
"version": "0.12.11",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "297cb2458a96ea96d5e9d6ef38f1b7305c071f32"
"reference": "ca5f2b7cf81c6d8fba74f9576970399c5817e03b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/297cb2458a96ea96d5e9d6ef38f1b7305c071f32",
"reference": "297cb2458a96ea96d5e9d6ef38f1b7305c071f32",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/ca5f2b7cf81c6d8fba74f9576970399c5817e03b",
"reference": "ca5f2b7cf81c6d8fba74f9576970399c5817e03b",
"shasum": ""
},
"require": {
@ -466,7 +471,7 @@
"MIT"
],
"description": "PHPStan - PHP Static Analysis Tool",
"time": "2020-02-04T22:30:27+00:00"
"time": "2020-02-16T14:00:29+00:00"
},
{
"name": "phpstan/phpstan-phpunit",

View File

@ -81,6 +81,7 @@ use pocketmine\item\Durable;
use pocketmine\item\enchantment\EnchantmentInstance;
use pocketmine\item\enchantment\MeleeWeaponEnchantment;
use pocketmine\item\Item;
use pocketmine\item\MaybeConsumable;
use pocketmine\item\WritableBook;
use pocketmine\item\WrittenBook;
use pocketmine\lang\TextContainer;
@ -2450,7 +2451,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
case InventoryTransactionPacket::USE_ITEM_ACTION_CLICK_AIR:
if($this->isUsingItem()){
$slot = $this->inventory->getItemInHand();
if($slot instanceof Consumable){
if($slot instanceof Consumable and !($slot instanceof MaybeConsumable and !$slot->canBeConsumed())){
$ev = new PlayerItemConsumeEvent($this, $slot);
if($this->hasItemCooldown($slot)){
$ev->setCancelled();

View File

@ -31,6 +31,6 @@ if(defined('pocketmine\_VERSION_INFO_INCLUDED')){
const _VERSION_INFO_INCLUDED = true;
const NAME = "PocketMine-MP";
const BASE_VERSION = "3.11.6";
const BASE_VERSION = "3.11.7";
const IS_DEVELOPMENT_BUILD = true;
const BUILD_NUMBER = 0;

View File

@ -55,7 +55,7 @@ abstract class Crops extends Flowable{
$this->getLevel()->setBlock($this, $ev->getNewState(), true, true);
}
$item->count--;
$item->pop();
return true;
}

View File

@ -99,7 +99,7 @@ class Grass extends Solid{
public function onActivate(Item $item, Player $player = null) : bool{
if($item->getId() === Item::DYE and $item->getDamage() === 0x0F){
$item->count--;
$item->pop();
TallGrassObject::growGrass($this->getLevel(), $this, new Random(mt_rand()), 8, 2);
return true;

View File

@ -72,7 +72,7 @@ class Sapling extends Flowable{
//TODO: change log type
Tree::growTree($this->getLevel(), $this->x, $this->y, $this->z, new Random(mt_rand()), $this->getVariant());
$item->count--;
$item->pop();
return true;
}

View File

@ -62,7 +62,7 @@ class Sugarcane extends Flowable{
$this->getLevel()->setBlock($this, $this, true);
}
$item->count--;
$item->pop();
return true;
}

View File

@ -38,6 +38,7 @@ use pocketmine\item\Durable;
use pocketmine\item\enchantment\Enchantment;
use pocketmine\item\FoodSource;
use pocketmine\item\Item;
use pocketmine\item\MaybeConsumable;
use pocketmine\item\Totem;
use pocketmine\level\Level;
use pocketmine\nbt\NBT;
@ -50,6 +51,7 @@ use pocketmine\network\mcpe\protocol\ActorEventPacket;
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
use pocketmine\network\mcpe\protocol\LevelEventPacket;
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
use pocketmine\network\mcpe\protocol\MovePlayerPacket;
use pocketmine\network\mcpe\protocol\PlayerListPacket;
use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
@ -295,6 +297,10 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
}
public function consumeObject(Consumable $consumable) : bool{
if($consumable instanceof MaybeConsumable and !$consumable->canBeConsumed()){
return false;
}
if($consumable instanceof FoodSource){
if($consumable->requiresHunger() and !$this->isHungry()){
return false;
@ -819,6 +825,23 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
}
}
public function broadcastMovement(bool $teleport = false) : void{
//TODO: workaround 1.14.30 bug: MoveActor(Absolute|Delta)Packet don't work on players anymore :(
$pk = new MovePlayerPacket();
$pk->entityRuntimeId = $this->getId();
$pk->position = $this->getOffsetPosition($this);
$pk->yaw = $this->yaw;
$pk->pitch = $this->pitch;
$pk->headYaw = $this->yaw;
$pk->mode = $teleport ? MovePlayerPacket::MODE_TELEPORT : MovePlayerPacket::MODE_NORMAL;
//we can't assume that everyone who is using our chunk wants to see this movement,
//because this human might be a player who shouldn't be receiving his own movement.
//this didn't matter when we were able to use MoveActorPacket because
//the client just ignored MoveActor for itself, but it doesn't ignore MovePlayer for itself.
$this->server->broadcastPacket($this->hasSpawned, $pk);
}
public function close() : void{
if(!$this->closed){
if($this->inventory !== null){

View File

@ -37,6 +37,7 @@ use pocketmine\item\Consumable;
use pocketmine\item\Durable;
use pocketmine\item\enchantment\Enchantment;
use pocketmine\item\Item;
use pocketmine\item\MaybeConsumable;
use pocketmine\math\Vector3;
use pocketmine\math\VoxelRayTrace;
use pocketmine\nbt\tag\ByteTag;
@ -111,7 +112,7 @@ abstract class Living extends Entity implements Damageable{
$this->setHealth($health);
/** @var CompoundTag[]|ListTag|null */
/** @var CompoundTag[]|ListTag|null $activeEffectsTag */
$activeEffectsTag = $this->namedtag->getListTag("ActiveEffects");
if($activeEffectsTag !== null){
foreach($activeEffectsTag as $e){
@ -359,6 +360,10 @@ abstract class Living extends Entity implements Damageable{
* etc.
*/
public function consumeObject(Consumable $consumable) : bool{
if($consumable instanceof MaybeConsumable and !$consumable->canBeConsumed()){
return false;
}
foreach($consumable->getAdditionalEffects() as $effect){
$this->addEffect($effect);
}

View File

@ -246,18 +246,18 @@ abstract class BaseInventory implements Inventory{
}
public function canAddItem(Item $item) : bool{
$item = clone $item;
$count = $item->getCount();
for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
$slot = $this->getItem($i);
if($item->equals($slot)){
if(($diff = $slot->getMaxStackSize() - $slot->getCount()) > 0){
$item->setCount($item->getCount() - $diff);
$count -= $diff;
}
}elseif($slot->isNull()){
$item->setCount($item->getCount() - $this->getMaxStackSize());
$count -= $this->getMaxStackSize();
}
if($item->getCount() <= 0){
if($count <= 0){
return true;
}
}

View File

@ -33,7 +33,7 @@ use pocketmine\event\player\PlayerBucketFillEvent;
use pocketmine\math\Vector3;
use pocketmine\Player;
class Bucket extends Item implements Consumable{
class Bucket extends Item implements MaybeConsumable{
public function __construct(int $meta = 0){
parent::__construct(self::BUCKET, $meta, "Bucket");
}

View File

@ -565,7 +565,7 @@ class Item implements ItemIds, \JsonSerializable{
/**
* Pops an item from the stack and returns it, decreasing the stack count of this item stack by one.
*
* @return $this
* @return static A clone of this itemstack containing the amount of items that were removed from this stack.
* @throws \InvalidArgumentException if trying to pop more items than are on the stack
*/
public function pop(int $count = 1) : Item{

View File

@ -0,0 +1,36 @@
<?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);
namespace pocketmine\item;
/**
* Items which are sometimes (but not always) consumable should implement this interface.
*
* Note: If implementing custom items, consider making separate items instead of using this.
* This interface serves as a workaround for the consumability of buckets and shouldn't
* really be used for anything else.
*/
interface MaybeConsumable extends Consumable{
public function canBeConsumed() : bool;
}

View File

@ -96,7 +96,7 @@ class PaintingItem extends Item{
$entity = Entity::createEntity("Painting", $blockReplace->getLevel(), $nbt);
if($entity instanceof Entity){
--$this->count;
$this->pop();
$entity->spawnToAll();
$player->getLevel()->broadcastLevelEvent($blockReplace->add(0.5, 0.5, 0.5), LevelEventPacket::EVENT_SOUND_ITEMFRAME_PLACE); //item frame and painting have the same sound

View File

@ -54,7 +54,7 @@ abstract class ProjectileItem extends Item{
$projectile->setMotion($projectile->getMotion()->multiply($this->getThrowForce()));
}
$this->count--;
$this->pop();
if($projectile instanceof Projectile){
$projectileEv = new ProjectileLaunchEvent($projectile);

View File

@ -44,7 +44,7 @@ class SpawnEgg extends Item{
$entity = Entity::createEntity($this->meta, $player->getLevel(), $nbt);
if($entity instanceof Entity){
--$this->count;
$this->pop();
$entity->spawnToAll();
return true;
}

View File

@ -264,7 +264,7 @@ class Level implements ChunkManager, Metadatable{
private $chunksPerTick;
/** @var bool */
private $clearChunksOnTick;
/** @var \SplFixedArray<Block> */
/** @var \SplFixedArray<Block|null> */
private $randomTickBlocks;
/** @var LevelTimings */
@ -1042,7 +1042,7 @@ class Level implements ChunkManager, Metadatable{
}
/**
* @phpstan-return \SplFixedArray<Block>
* @phpstan-return \SplFixedArray<Block|null>
*/
public function getRandomTickedBlocks() : \SplFixedArray{
return $this->randomTickBlocks;

View File

@ -548,6 +548,7 @@ class Chunk{
*/
public function setLightPopulated(bool $value = true){
$this->lightPopulated = $value;
$this->hasChanged = true;
}
public function isPopulated() : bool{
@ -559,6 +560,7 @@ class Chunk{
*/
public function setPopulated(bool $value = true){
$this->terrainPopulated = $value;
$this->hasChanged = true;
}
public function isGenerated() : bool{
@ -570,6 +572,7 @@ class Chunk{
*/
public function setGenerated(bool $value = true){
$this->terrainGenerated = $value;
$this->hasChanged = true;
}
/**

View File

@ -98,23 +98,27 @@ class PopulationTask extends AsyncTask{
$manager->setChunk($chunk->getX(), $chunk->getZ(), $chunk);
if(!$chunk->isGenerated()){
$generator->generateChunk($chunk->getX(), $chunk->getZ());
$chunk = $manager->getChunk($chunk->getX(), $chunk->getZ());
$chunk->setGenerated();
}
foreach($chunks as $c){
foreach($chunks as $i => $c){
$manager->setChunk($c->getX(), $c->getZ(), $c);
if(!$c->isGenerated()){
$generator->generateChunk($c->getX(), $c->getZ());
$c->setGenerated();
$chunks[$i] = $manager->getChunk($c->getX(), $c->getZ());
$chunks[$i]->setGenerated();
}
}
$generator->populateChunk($chunk->getX(), $chunk->getZ());
$chunk = $manager->getChunk($chunk->getX(), $chunk->getZ());
$chunk->setPopulated();
$chunk->recalculateHeightMap();
$chunk->populateSkyLight();
$chunk->setLightPopulated();
$chunk->setPopulated();
$this->chunk = $chunk->fastSerialize();
foreach($chunks as $i => $c){

View File

@ -100,7 +100,7 @@ class NetworkBinaryStream extends BinaryStream{
$capeId = $this->getString();
$fullSkinId = $this->getString();
return new SkinData($skinId, $skinResourcePatch, $skinData, $animations, $capeData, $geometryData, $animationData, $premium, $persona, $capeOnClassic, $capeId);
return new SkinData($skinId, $skinResourcePatch, $skinData, $animations, $capeData, $geometryData, $animationData, $premium, $persona, $capeOnClassic, $capeId, $fullSkinId);
}
/**
@ -123,9 +123,7 @@ class NetworkBinaryStream extends BinaryStream{
$this->putBool($skin->isPersona());
$this->putBool($skin->isPersonaCapeOnClassic());
$this->putString($skin->getCapeId());
//this has to be unique or the client will do stupid things
$this->putString(UUID::fromRandom()->toString()); //full skin ID
$this->putString($skin->getFullSkinId());
}
private function getSkinImage() : SkinImage{

View File

@ -39,6 +39,12 @@ abstract class DataPacket extends NetworkBinaryStream{
public const NETWORK_ID = 0;
public const PID_MASK = 0x3ff; //10 bits
private const SUBCLIENT_ID_MASK = 0x03; //2 bits
private const SENDER_SUBCLIENT_ID_SHIFT = 10;
private const RECIPIENT_SUBCLIENT_ID_SHIFT = 12;
/** @var bool */
public $isEncoded = false;
/** @var CachedEncapsulatedPacket|null */
@ -92,10 +98,13 @@ abstract class DataPacket extends NetworkBinaryStream{
* @throws \UnexpectedValueException
*/
protected function decodeHeader(){
$pid = $this->getUnsignedVarInt();
$header = $this->getUnsignedVarInt();
$pid = $header & self::PID_MASK;
if($pid !== static::NETWORK_ID){
throw new \UnexpectedValueException("Expected " . static::NETWORK_ID . " for packet ID, got $pid");
}
$this->senderSubId = ($header >> self::SENDER_SUBCLIENT_ID_SHIFT) & self::SUBCLIENT_ID_MASK;
$this->recipientSubId = ($header >> self::RECIPIENT_SUBCLIENT_ID_SHIFT) & self::SUBCLIENT_ID_MASK;
}
/**
@ -123,7 +132,11 @@ abstract class DataPacket extends NetworkBinaryStream{
* @return void
*/
protected function encodeHeader(){
$this->putUnsignedVarInt(static::NETWORK_ID);
$this->putUnsignedVarInt(
static::NETWORK_ID |
($this->senderSubId << self::SENDER_SUBCLIENT_ID_SHIFT) |
($this->recipientSubId << self::RECIPIENT_SUBCLIENT_ID_SHIFT)
);
}
/**

View File

@ -196,7 +196,7 @@ class PacketPool{
*/
public static function getPacket(string $buffer) : DataPacket{
$offset = 0;
$pk = static::getPacketById(Binary::readUnsignedVarInt($buffer, $offset));
$pk = static::getPacketById(Binary::readUnsignedVarInt($buffer, $offset) & DataPacket::PID_MASK);
$pk->setBuffer($buffer, $offset);
return $pk;

View File

@ -48,9 +48,9 @@ class UpdateTradePacket extends DataPacket{
/** @var string */
public $displayName;
/** @var bool */
public $isWilling;
/** @var bool */
public $isV2Trading;
/** @var bool */
public $isWilling;
/** @var string */
public $offers;
@ -62,8 +62,8 @@ class UpdateTradePacket extends DataPacket{
$this->traderEid = $this->getEntityUniqueId();
$this->playerEid = $this->getEntityUniqueId();
$this->displayName = $this->getString();
$this->isWilling = $this->getBool();
$this->isV2Trading = $this->getBool();
$this->isWilling = $this->getBool();
$this->offers = $this->getRemaining();
}
@ -75,8 +75,8 @@ class UpdateTradePacket extends DataPacket{
$this->putEntityUniqueId($this->traderEid);
$this->putEntityUniqueId($this->playerEid);
$this->putString($this->displayName);
$this->putBool($this->isWilling);
$this->putBool($this->isV2Trading);
$this->putBool($this->isWilling);
$this->put($this->offers);
}

View File

@ -23,6 +23,8 @@ declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types;
use pocketmine\utils\UUID;
class SkinData{
/** @var string */
@ -47,11 +49,13 @@ class SkinData{
private $personaCapeOnClassic;
/** @var string */
private $capeId;
/** @var string */
private $fullSkinId;
/**
* @param SkinAnimation[] $animations
*/
public function __construct(string $skinId, string $resourcePatch, SkinImage $skinImage, array $animations = [], SkinImage $capeImage = null, string $geometryData = "", string $animationData = "", bool $premium = false, bool $persona = false, bool $personaCapeOnClassic = false, string $capeId = ""){
public function __construct(string $skinId, string $resourcePatch, SkinImage $skinImage, array $animations = [], SkinImage $capeImage = null, string $geometryData = "", string $animationData = "", bool $premium = false, bool $persona = false, bool $personaCapeOnClassic = false, string $capeId = "", ?string $fullSkinId = null){
$this->skinId = $skinId;
$this->resourcePatch = $resourcePatch;
$this->skinImage = $skinImage;
@ -63,6 +67,8 @@ class SkinData{
$this->persona = $persona;
$this->personaCapeOnClassic = $personaCapeOnClassic;
$this->capeId = $capeId;
//this has to be unique or the client will do stupid things
$this->fullSkinId = $fullSkinId ?? UUID::fromRandom()->toString();
}
public function getSkinId() : string{
@ -112,4 +118,7 @@ class SkinData{
return $this->capeId;
}
public function getFullSkinId() : string{
return $this->fullSkinId;
}
}

View File

@ -0,0 +1,42 @@
<?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);
namespace pocketmine\network\mcpe\protocol;
use PHPUnit\Framework\TestCase;
class DataPacketTest extends TestCase{
public function testHeaderFidelity() : void{
$pk = new TestPacket();
$pk->senderSubId = 3;
$pk->recipientSubId = 2;
$pk->encode();
$pk2 = new TestPacket();
$pk2->setBuffer($pk->getBuffer());
$pk2->decode();
self::assertSame($pk2->senderSubId, 3);
self::assertSame($pk2->recipientSubId, 2);
}
}

View File

@ -0,0 +1,34 @@
<?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);
namespace pocketmine\network\mcpe\protocol;
use pocketmine\network\mcpe\NetworkSession;
class TestPacket extends DataPacket{
public const NETWORK_ID = 1023;
public function handle(NetworkSession $handler) : bool{
return false;
}
}